笔记-iOS中级教程多线程

资料来源:腾讯课堂=>《[iOS]iOS中级教程多线程》

09 __bridge

pthread_t pthread;

//char *name = "zs";
//int result = pthread_create(&pthread, NULL, demo, name);

//------使用OC语言
NSString *name = @"zs";
//__bridge 桥接
//MRC中内存管理原则:谁申请,水释放
//ARC中自动给OC对象,添加retain release autorelease
//把OC中的对象传给c语言的函数,要桥接;同样的,把c语言的参数传给OC也要桥接
int result = pthread_create(&pthread, NULL, demo, (__bridge void *)(name));
//demo函数
void * (*demo)(void * param){
NSString *name = (__bridge NSString *)param;
//NSLog(@"hello %s, %@", param, [NSThread currentThread]);
NSLog(@"hello %@, %@", name, [NSThread currentThread]);
}

__bridge告诉函数pthread_create,在ARC中传入的参数name需要函数来负责release

10 NSThread

3种创建方式:

//方法一:需要调用start方法开启线程
NSThread *thread = [[NSThread alloc] initWithTarget: self selector: @selector(demo) object: nil];
[thread start];
//方法二:类方法
[NSThread detachNewThreadSelector: @selector(demo) toTarget: self withObject: nil];
//方法三:严格来说不算是NSThread方法
[self performSelectorInBackground: @selector(demo) withObject: nil];

11 线程状态

//当线程结束之后,不能再次使用

//新建状态
NSThread *thread = [[NSThread alloc] initWithTarget: self selector: @selector(demo) object: nil];
//就绪状态
[thread start];

-(void)demo{
  for (int i = 0; i < 20; i ++){
    NSLog(@"%d", i);
    if (i == 5){
      //阻塞状态
      [NSThread sleepForTimeInterval: 3];
    }
    if (i == 10){
      //线程退出 死亡状态
      [NSThread exit];
    }
  }
}

12 线程属性

NSThread *thread = [[NSThread alloc] initWithTarget: self selector: @selector(demo) object: nil];
//线程名称
thread.name = @"t1";
//线程优先级,0-1.0,default:0.5
//内核调度算法在决定该运行哪个线程时,会把线程的优先级作为考量因素,较高优先级的线程会比较低优先级的线程具有更多的运行机会。较高优先级不保证你的线程具体执行的时间,只是相比较低优先级的线程它更有可能被调度器选择执行而已。
//即无法保证thread执行完再执行其他线程
thread.threadPriority = 1.0;
[thread start];

15 互斥锁

//任意一个对象内部都有一把锁,锁默认是打开的
//加锁会影响程序的性能

//互斥锁
//线程同步
/*
NSObject *obj = [NSObject new];
@synchronized(obj){
  //此时,使用obj局部变量的话,thread1进来默认objc的锁是打开的,程序可以继续进行;然后thread2进来,又重新初始化了一个objc,锁默认也是打开的,因此程序也可继续进行,无法达到加锁的效果;
  //改进办法:将objc设置成全局变量或者属性
}
*/
//用的是self的锁
@synchronized(self){
  if (self.ticketsCount > 0)
    self.ticketsCount --;
}else{
  NSLog(@"来晚啦,票没了");
}

互斥锁使用:@synchronize(锁对象){//需要锁定的代码}

互斥锁:能有效防止因多线程抢夺资源造成的数据安全问题

线程同步的意思是:多条线程按顺序地执行任务。互斥锁就是使用了线程同步技术


16 原子属性

属性中的修饰符:

  • nonatomic 非原子属性
  • atomic 原子属性(线程安全),针对多线程设计的,默认值。保证同一时间只有一个线程能够写入,但是同一时间多个线程都可以取值。atomic本身就有一把锁(自旋锁),单写多读:单个线程写入,多个线程可以读取。

17 互斥锁和自旋锁的区别

互斥锁:如果发现其他线程正在执行锁定代码,线程会进入休眠(就绪状态),等其他线程时间片到打开锁后,线程会被唤醒(执行)

自旋锁:如果发现有其他线程正在锁定代码,线程会用死循环的方式,一直等待锁定的代码执行完成,自旋锁更适合执行不耗时的代码。一般就用在属性中。

线程安全:线程同时操作时不安全的,多个线程同时操作一个全局变量。线程安全,即是在多个贤臣进行读写操作时,仍然能够保证数据的正确。

主线程(UI线程):

  • 几乎所有UIKit提供的类都是线程不安全的,<font color=red>所有更新UI的操作都在主线程上执行</font>。例如,更新label的值,如果多个线程都要操作label则无法保证线程安全,因此把UI操作都放在主线程,即执行完一样再执行另一样,保证安全。
  • 所有包含Mutable的类都是线程不安全的。

18 异步下载网络图片

-(void)loadView
{
    //初始化scrollView,并赋值给当前vc的view
    self.scrollView = [[UIScrollView alloc] initWithFrame: [UIScreen mainScreen].bounds];
    self.scrollView.backgroundColor = [UIColor whiteColor];
    self.view = self.scrollView;
    //初始化imageView
    self.imageView = [[UIImageView alloc] init];
    [self.scrollView addSubview: self.imageView];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    NSThread *thread = [[NSThread alloc] initWithTarget: self selector: @selector(downloadImage) object: nil];
    [thread start];
}

-(void)downloadImage
{
    NSData *data = [NSData dataWithContentsOfURL: [NSURL URLWithString: @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1583403375529&di=765d6a01b4b7a5183862ad886f5a2f5d&imgtype=0&src=http%3A%2F%2Fpic1.win4000.com%2Fwallpaper%2F2017-11-29%2F5a1e130e127ef.jpg"]];
    UIImage *image = [UIImage imageWithData: data];
    
    //在主线程上更新UI控件  线程间通信
    //waitUntilDone 值是YES 会等待方法执行完毕,才会执行后续代码
    [self performSelectorOnMainThread: @selector(updateUI:) withObject: image waitUntilDone: YES];
    
}

-(void)updateUI: (UIImage *)image
{
    self.imageView.image = image;
    //    self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
    //让imageView的大小和图片一致
    [self.imageView sizeToFit];
    //设置scrollView的滚动范围
    self.scrollView.contentSize = image.size;
}

19 strong和weak

什么时候用strong和weak

  • OC对象用strong
  • UI控件用连线的时候用weak,因为拖拽的时候相当于[self.view addSubview: **],即又一次强引用了;使用strong也可以,只是self对控件又加了一次强引用,不会导致循环引用,但在释放时要释放两次。

示例:

创建ZYPerson类,整体工程是ARC模式的,设置ZYPerson为MRC

那么,秉持谁申请谁释放的原则,将person放入自动释放池,延迟释放。

+(instancetype)personWithName:(NSString *)name
{
    ZYPerson *person = [[ZYPerson new] autorelease];
    person.name = name;
    return person;
}

在vc中绑定属性,特地设置为weak

@property (nonatomic, weak) ZYPerson *p1;
@property (nonatomic, weak) ZYPerson *p2;

viewDidLoad中初始化p1和p2属性

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.p1 = [[ZYPerson alloc] init];
    self.p1.name = @"zs";
    NSLog(@"p1: %@", self.p1.name);
    
    self.p2 = [ZYPerson personWithName: @"ls"];
    NSLog(@"p2: %@", self.p2.name);
}

结果为:


原因:p2采用的初始化方法中,自动释放池对p2有强引用,因此可以打印出ls。在viewDidLoad()结束后自动释放池销毁时,p2也就release了。因此如果在其他方法中再次调用self.p2.name也会得到null的结果。

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"p1: %@", self.p1.name);
    NSLog(@"p2: %@", self.p2.name);
}

20 自动释放池

iOS开放中的内存管理:

  • iOS开发中,没有JAVA或C#中的垃圾回收机制
  • 在MRC中对象谁申请,谁释放
  • 使用ARC开发,只是在编译时,编译器会根据代码结构自动添加retain, release和autorelease

自动释放池:

  • 标记为autorelease的对象,会被添加到最近一次创建的自动释放池中
  • 当自动释放池被销毁或耗尽时,会向池中所有对象发送release消息

每一次主线程的消息循环开始的时候,系统会先创建自动释放池,消息循环结束前,会释放自动释放池。消息循环(the event loop)是用来处理事件的。

自动释放池和线程的关系,其实是因为消息循环和线程有关。

示例中viewDidLoad()方法中的是在application:didFinishLaunchingWithOptions事件里执行的,因此自动释放池也是在此事件中创建和释放的。

什么时候使用自动释放池:

  • 如果你写了一个循环,其内部创建了很多临时对象,你需要在循环内部创建自动释放池用来销毁这些对象。
  • 如果你生成一个子线程,你必须创建你自己的自动释放池,并且是在线程开始的时候尽快创建autoreleasepool,否则会造成内存泄露(该释放没释放;野指针:不该释放的释放了

21 自动释放池面试题

for (int i = 0; i < 100000000;i ++){
  @autoreleasepool{
      NSString *str = [[NSString alloc] initWithFormat: @"%d", i];
  }
}

加上自动释放池后,内存几乎不涨。

22 属性的修饰符

属性修饰符:

retain: MRC中使用

strong: ARC中使用

weak: 只有ARC下才能用

assign: ARC和MRC都可以使用

copy:ARC和MRC都可以使用

  • 字符串为什么要用copy:

    举例:

    NSMutableString *string = [NSMutableString string];
    [string appendString: @"hello"];
    

    如果使用strong

    @property (nonatomic, strong) NSString *name;
    //赋值
    self.name = string;
    

    再修改string的值

    [string appendString: @" zs"];
    

    结果得到name也是"hello zs"。因为*表示地址,对name赋值时相当于指向string的地址,因此string更改,name也跟着改变。但这不是我们想要的,name作为字符串被赋值后,我们希望它保持这个值,下面来看看copy

    @property (nonatomic, copy) NSString *name;
    //赋值
    self.name = string;
    

    修改string的值后打印name依然是"hello"

    原理:使用copy时编译器会帮我们做一件事,赋值时对string进行copy。相当于

    @property (nonatomic, strong) NSString *name;
    //赋值
    self.name = [string copy];
    
  • block作为属性的时候,为什么要用copy

    第一种block 全局Block __NSGlobalBlock__

    void (^demo)() = ^{
      NSLog(@"aaa");
    };
    NSLog(@"%@", demo);
    

    第二种block 栈Block __NSStackBlock__,环境MRC

    int number = 3;
    void (^demo)() = ^{
      NSLog(@"aaa %d", number);
    };
    NSLog(@"%@", demo);
    

    第三种block 堆Block __NSMallocBlock__,环境MRC

    int number = 3;
    void (^demo)() = ^{
      NSLog(@"aaa %d", number);
    };
    NSLog(@"%@", [demo copy]);
    

    一般我们使用的最多的是第二种block,在MRC中定义block属性如下

    @property (nonatomic, assign) void(^myBlock)();
    

    给myBlock赋值

    -(void)test{
      int n = 5;
      [self setMyBlock: ^{
        NSLog(@"%d", n);
      }];
      NSLog(@"%@", self.myBlock);
    }
    

    使用myBlock

    [self test];
    self.myBlock();
    self.myBlock();//第二次调用的时候报错,因为myBlock是在栈空间,test作用域外即被释放。
    

    因此使用block作为属性时使用copy,copy过的block被储存在堆上,如第三种block。

  • delegate为什么要用weak修饰

    self.person = [Person new];
    self.person.delegate = self;
    

    如果delegate的修饰符是strong,导致循环引用

    vc-->person-->delegate-->self(vc)

  • weak和assign的区别

    举例,分别用weak和assign修饰oc对象

    @property (nonatomic, weak) Person *weakPerson;
    @property (nonatomic, assign) Person *assignPerson
    

    初始化并复制

    self.weakPerson = [Person new];//1
    self.weakPerson.name = @"zs";//2
    NSLog(@"%@", self.weakPerson.name);
    
    self.assignPerson = [Person new];//3
    self.assignPerson.name = @"ls";//4
    NSLog(@"%@", self.assignPerson.name);
    

    结果,程序在self.assignPerson.name = @"ls";处崩溃,报野指针错误。

    原因:weakPerson初始化时(1)在堆上开辟内存空间存放weakPerson对象,栈上开辟内存空间存放堆上的内存地址;执行到2时由于没有强引用,堆上的对象被销毁,栈上的指针指向内存地址为0的nil对象。因此2是向空对象发送对象,得到null。assignPerson执行3时和1一样,执行4时因为没有强引用堆上的assignPerson对象被销毁,但是栈上的指针指向地址没有变化,但是对应地址的对象已经销毁,才报野指针错误。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 1.属性readwrite,readonly,assign,retain,copy,nonatomic 各是什么作...
    曾令伟阅读 1,042评论 0 10
  • 把网上的一些结合自己面试时遇到的面试题总结了一下,以后有新的还会再加进来。 1. OC 的理解与特性 OC 作为一...
    AlaricMurray阅读 2,538评论 0 20
  • Oc基础 1、#import的用途 1>#import与#include一样,拷贝文件的内容 2>可以自动防止文件...
    开着保时捷堵你家门口阅读 866评论 0 0
  • 这一次,我们来介绍一款小米生态链产品:米家签字笔。 我刚得知这件产品的时候,就非常吃惊:哇,一款普通的签字笔,售价...
    吴腾Reed阅读 1,320评论 1 1
  • 小时候钱是一块糖 有了糖就很幸福 那时候幸福也很简单 长大一点了钱就是一身合适的衣服 有了它就很满足 幸福就在不远...
    金枝玉叶_0ada阅读 200评论 0 6