iOS-Block的总结

来源:互联网 发布:薛家 知乎 编辑:程序博客网 时间:2024/05/22 16:59

前言: 前面学习了那么多block的知识, 其实就为了解决项目中的几个问题

1. ARC 与 MRC下__block的区别
2. __block 和 __weak的区别
3. block内嵌的注意事项
4. block使用场景中的block块中, 引用self是否使用__weak或__block

1. ARC 与 MRC下__block的区别

  • 在 MRC 下,使用 __block 说明符也可以避免循环引用。因为当 block 从栈拷贝到堆时,__block 对象类型的变量不会被 retain,没有 __block 说明符的对象类型的变量则会被 retian
  • 在ARC下, 使用__block会retain修饰的对象类型, 防止循环引用可使用__weak(iOS5.0才有)或__unsafe_unretain

2. __block 和 __weak的区别

__weak specifies a reference that does not keep the
referenced object alive. A weak reference is set to nil when
there are no strong references to the object.

使用了__weak修饰符的对象,作用等同于定义为weak的property。自然不会导致循环引用问题,因为苹果文档已经说的很清楚,当原对象没有任何强引用的时候,弱引用指针也会被设置为nil。

因此,__block和__weak修饰符的区别其实是挺明显的:

  • ARC下__weak修饰的对象在改对象销毁后也会被销毁, 并置为nil, 但是__block修饰的对象在该对象销毁后仍然存在, 所以容易造成循环引用
  • __block不管是ARC还是MRC模式下都可以使用,可以修饰对象,还可以修饰基本数据类型。
  • __weak只能在ARC模式下使用,也只能修饰对象(NSString),不能修饰基本数据类型(int)。
  • __block对象可以在block中被重新赋值,__weak不可以。
  • __block对象在ARC下可能会导致循环引用,非ARC下会避免循环引用,__weak只在ARC下使用,可以避免循环引用。

PS:__unsafe_unretained修饰符可以被视为iOS SDK 4.3以前版本的__weak的替代品,不过不会被自动置空为nil。所以尽可能不要使用这个修饰符。

3. block内嵌block的注意事项

在block里面使用block,比如在GCD的block里内嵌另一个block。因为GCD本身会拷贝传入dispatch_queue中的block,内嵌的block也会同时被拷贝:

When you copy a block, any references to other blocks from within that block are copied if necessary—an entire tree may be copied (from the top). If you have block variables and you reference a block from within the block, that block will be copied.

另外当block作为NSDictionary的key时,这些key都是默认copy的。原因很简单:NSDictionary是通过key hash拿到的值进行查询,如果不对key进行拷贝,则在进行查询时会因为key的变化而导致查询失败。(所以也不太推荐拿一些复杂易变的对象当作NSDictionary的key)

4. block使用场景中的block块中, 引用self是否使用__weak或__block

AFN GCD Masonry

将Block作为参数传给AFN, dispatch_async, Masonry时,系统会将Block拷贝到堆上,如果Block中使用了实例变量,还将retain self,因为AFN, dispatch_async, Masonry并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,AFN, dispatch_async, Masonry必须自己retain一次self,任务完成后再release self。但这里使用__weak,使AFN, dispatch_async, Masonry没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash。

//#import "Person.h"- (void)say {    __weak typeof(self) weakSelf = self;    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){        NSLog(@"%@", weakSelf);    });}//#import "ViewController.h"//#import "Person.h"Person *person = [[Person alloc] init];[person say];

这里用dispatch_after模拟了一个异步任务,10秒后执行Block。但执行Block的时候Person *person已经被释放了,打印为
2016-03-15 15:14:16.048 TestBlock[3588:143295] (null),
导致crash。解决办法是不要使用__weak

控制器间

5. Block实例注意

使用__block与不使用__block的比较:

    typedef long (^BlkSum)(int, int);    int base = 100;    BlkSum sum = ^ long (int a, int b) {        return base + a + b;    };    base++;    printf("%ld\n",sum(1,2));   // result: 103    printf("%d\n",base);        // result: 101
    typedef long (^BlkSum)(int, int);    __block int base = 100;    BlkSum sum = ^ long (int a, int b) {        return base + a + b;    };    base++;    printf("%ld\n",sum(1,2));   // result: 104    printf("%d\n",base);        // result: 101
  • 使用__block修饰变量时,将取变量此刻运行时的值,而不是定义时的值。这个例子中,执行sum(1,2)时,base将取base++之后的值,也就是101,再执行 base+a+b,运行结果是104。执行完Block时,base已经变成101了。
  • 不使用__block修饰变量时,将取变量定义时的值。这个例子中,执行sum(1,2)时是100,再执行 base+a+b,运行结果是103。

在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和NSConcreteMallocBlock类型的 block。

原本的 NSConcreteStackBlock 的 block 会被 NSConcreteMallocBlock 类型的 block 替代, 因此使用strong和copy修饰block属性区别不大, 但是官方还是建议使用copy修饰

1 0
原创粉丝点击