iOS-Block的总结

释放双眼,带上耳机,听听看~!

前言: 前面学习了那么多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("%ldn",sum(1,2));   // result: 103
printf("%dn",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("%ldn",sum(1,2));   // result: 104
printf("%dn",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修饰

人已赞赏
iOS文章

iOS Universal Links 接入指南

2020-3-4 1:07:14

iOS文章

iOS-UIKit继承图

2020-3-4 4:08:31

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
有新消息 消息中心
搜索