iOS-Block的总结
2016-03-13 22:55
453 查看
前言: 前面学习了那么多block的知识, 其实就为了解决项目中的几个问题
1. ARC 与 MRC下__block的区别
2. __block 和 __weak的区别
3. block内嵌的注意事项
4. block使用场景中的block块中, 引用self是否使用__weak或__block
在ARC下, 使用__block会retain修饰的对象类型, 防止循环引用可使用__weak(iOS5.0才有)或__unsafe_unretain
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:
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)
这里用dispatch_after模拟了一个异步任务,10秒后执行Block。但执行Block的时候Person *person已经被释放了,打印为
导致crash。解决办法是不要使用__weak
使用__block修饰变量时,将取变量
不使用__block修饰变量时,将取变量定义时的值。这个例子中,执行sum(1,2)时是100,再执行 base+a+b,运行结果是103。
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 thereferenced 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修饰相关文章推荐
- iOS使用AVFoundation实现二维码扫描
- iOS 封装一个Model 处理返回时间
- iOS动画 过渡/转场动画的简单应用
- IOS 获取设备本地音视频
- IOS开发之OC学习笔记(上)
- ios核心动画 类图 一目了然
- iOS NSString 和NSData 转换 zhuanzai
- ios cell多选
- ios tableview单选
- iOS开发-进阶:JPush设置标签与别名的API
- iOS开发-进阶:开启Remote notifications
- iOS9 新特征之一:Bitcode
- iOS 学习路线整理(根据学习的深入,需要持续整理)
- iOS视频播放器
- iOS关灯游戏AI的设计和实现
- iOS基础开发练习 国际棋盘 的设计
- iOS之NSDictionary和NSArray以及NSMutableDictionary和NSMutableArray:将不再是问题
- iOS 消息推送
- 支付宝 iOS SDK 的简单使用
- OC基础