__weak与__block区别
来源:互联网 发布:.fun域名火了 编辑:程序博客网 时间:2024/05/18 15:07
公司最近在招 iOS,我面试了几个人,问到 block 避免循环引用的问题时,发现好多人都说通过添加 __block
修饰词来避免。再加上我对__block
和__weak
也没有区分的太明确,搞得我都有点儿怀疑我自己以前是不是用错了。正好借这个机会来一探究竟~
准备工作
首先我定义了一个类 MyObject
继承 NSObject
,并添加了一个属性 text,重写了description
方法,返回 text 的值。这个主要是因为编译器本身对 NSString 是有优化的,创建的 string 对象有可能是静态存储区永不释放的,为了避免使用 NSString 引起一些问题,还是创建一个 NSObject 对象比较合适。
另外我自定义了一个 TLog 方法输出对象相关值,定义如下:
#define TLog(prefix,Obj) {NSLog(@"变量内存地址:%p, 变量值:%p, 指向对象值:%@, --> %@",&Obj,Obj,Obj,prefix);}
__weak
我们测试下面一段代码
MyObject *obj = [[MyObject alloc]init];obj.text = @"111111111111";TLog(@"obj", obj);__weak MyObject *weakObj = obj;TLog(@"weakObj", weakObj);void(^testBlock)() = ^(){TLog(@"weakObj - block", weakObj);};testBlock();obj = nil;testBlock();
输出:
变量内存地址:0x7fff58c8a9f0, 变量值:0x7f8e0307f1d0, 指向对象值:111111111111, --> obj变量内存地址:0x7fff58c8a9e8, 变量值:0x7f8e0307f1d0, 指向对象值:111111111111, --> weakObj变量内存地址:0x7f8e030804c0, 变量值:0x7f8e0307f1d0, 指向对象值:111111111111, --> weakObj - block变量内存地址:0x7f8e030804c0, 变量值:0x0, 指向对象值:(null), --> weakObj - block
从上面的结果可以看到
- block 内的 weakObj 和外部的 weakObj 并不是同一个变量
- block 捕获了 weakObj 同时也是对 obj 进行了弱引用,当我在 block 外把 obj 释放了之后,block 内也读不到这个变量了
- 当 obj 赋值 nil 时,block 内部的 weakObj 也为 nil 了,也就是说 obj 实际上是被释放了,可见
__weak
是可以避免循环引用问题的
接下来我们再看第二段代码
MyObject *obj = [[MyObject alloc]init];obj.text = @"111111111111";TLog(@"obj", obj);__weak MyObject *weakObj = obj;TLog(@"weakObj-0", weakObj);dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{__strong MyObject *strongObj = weakObj;sleep(3);TLog(@"weakObj - block", weakObj);TLog(@"strongObj - block", strongObj);});sleep(1);obj = nil;TLog(@"weakObj-1", weakObj);sleep(4);TLog(@"weakObj-2", weakObj);
输出
变量内存地址:0x7fff572a9ab8, 变量值:0x7f9562f76210, 指向对象值:111111111111, --> obj变量内存地址:0x7fff572a9ab0, 变量值:0x7f9562f76210, 指向对象值:111111111111, --> weakObj-0变量内存地址:0x7fff572a9ab0, 变量值:0x7f9562f76210, 指向对象值:111111111111, --> weakObj-1变量内存地址:0x7f9562d143a0, 变量值:0x7f9562f76210, 指向对象值:111111111111, --> weakObj - block变量内存地址:0x116ccbe08, 变量值:0x7f9562f76210, 指向对象值:111111111111, --> strongObj - block变量内存地址:0x7fff572a9ab0, 变量值:0x0, 指向对象值:(null), --> weakObj-2
如果你看过 AFNetworking 的源码,会发现 AFN 中作者会把变量在 block 外面先用 __weak
声明,在 block 内把前面 weak 声明的变量赋值给 __strong
修饰的变量。这种写法的好处就是可以让变量在 block 内部安全可用,即使外部释放了,也会在 block 的生命周期内保留该变量。这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量。
__block
先上代码
MyObject *obj = [[MyObject alloc]init];obj.text = @"11111111111111";TLog(@"obj",obj);__block MyObject *blockObj = obj;obj = nil;TLog(@"blockObj -1",blockObj);void(^testBlock)() = ^(){TLog(@"blockObj - block",blockObj);MyObject *obj2 = [[MyObject alloc]init];obj2.text = @"222222222222";TLog(@"obj2",obj2);blockObj = obj2;TLog(@"blockObj - block",blockObj);};NSLog(@"%@",testBlock);TLog(@"blockObj -2",blockObj);testBlock();TLog(@"blockObj -3",blockObj);
结果
变量内存地址:0x7fff5021a9f0, 变量值:0x7ff6b48d8cd0, 指向对象值:11111111111111, --> obj变量内存地址:0x7fff5021a9e8, 变量值:0x7ff6b48d8cd0, 指向对象值:11111111111111, --> blockObj -1<__NSMallocBlock__: 0x7ff6b48d8c20>变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d8cd0, 指向对象值:11111111111111, --> blockObj -2变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d8cd0, 指向对象值:11111111111111, --> blockObj - block变量内存地址:0x7fff5021a7f8, 变量值:0x7ff6b48d9960, 指向对象值:222222222222, --> obj2变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d9960, 指向对象值:222222222222, --> blockObj - block变量内存地址:0x7ff6b48da518, 变量值:0x7ff6b48d9960, 指向对象值:222222222222, --> blockObj -3
我对 __block
的理解是其实际上是把变量的作用域给改变了,应该是提升了变量的作用域,使得在 block 内部和外部所访问的是同一个变量。类似于声明一个 static 和 global 变量的意思。
之所以代码中打印了一个 testBlock 的类型,是因为可以看到在 testBlock 声明前,blockObj 的地址和声明后的地址是有变化的,也就是说 blockObj 应该是在 block 内部提升的作用域。
再来看看 __block
能不能避免循环引用的问题
MyObject *obj = [[MyObject alloc]init];obj.text = @"11111111111111";TLog(@"obj",obj);__block MyObject *blockObj = obj;obj = nil;void(^testBlock)() = ^(){TLog(@"blockObj - block",blockObj);};obj = nil;testBlock();TLog(@"blockObj",blockObj);
输出
变量内存地址:0x7fff57eef9f0, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> obj变量内存地址:0x7ff86c918a88, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> blockObj - block变量内存地址:0x7ff86c918a88, 变量值:0x7ff86a55a160, 指向对象值:11111111111111, --> blockObj
当外部 obj 指向 nil 的时候,obj 理应被释放,但实际上 blockObj 依然强引用着 obj,obj 其实并没有被真正释放。因此使用 __block
并不能避免循环引用的问题。
但是我们可以通过手动释放 blockObj 的方式来释放 obj,这就需要我们在 block 内部将要退出的时候手动释放掉 blockObj ,如下这种形式
MyObject *obj = [[MyObject alloc]init];obj.text = @"11111111111111";TLog(@"obj",obj);__block MyObject *blockObj = obj;obj = nil;void(^testBlock)() = ^(){TLog(@"blockObj - block",blockObj);blockObj = nil;};obj = nil;testBlock();TLog(@"blockObj",blockObj);
这种形式既能保证在 block 内部能够访问到 obj,又可以避免循环引用的问题,但是这种方法也不是完美的,其存在下面几个问题
- 必须记住在 block 底部释放掉 block 变量,这其实跟 MRC 的形式有些类似了,不太适合 ARC
- 当在 block 外部修改了 blockObj 时,block 内部的值也会改变,反之在 block 内部修改 blockObj 在外部再使用时值也会改变。这就需要在写代码时注意这个特性可能会带来的一些隐患
__block
其实提升了变量的作用域,在 block 内外访问的都是同一个 blockObj 可能会造成一些隐患
总结
__weak
本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong
的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题
__block
本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block
修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。
扩展阅读
block使用小结、在arc中使用block、如何防止循环引用
转自:http://honglu.me/2015/01/06/weak%E4%B8%8Eblock%E5%8C%BA%E5%88%AB/
- __weak与__block区别
- __weak与__block区别
- __weak与__block的区别
- iOS __block 与 __weak区别
- __block 与 __weak的区别与使用
- __block 与 __weak的区别理解
- __block 与 __weak的区别理解
- __block与__weak的区别理解
- __weak与__block修饰符的区别
- __weak与__block修饰符区别
- __block 与 __weak的区别理解
- __weak和__block探究与区别
- __block 与 __weak的区别理解
- __block 与 __weak的区别理解
- __block 与 __weak的区别理解
- __block 与 __weak的区别理解 补充
- __block 与 __weak的区别理解
- __weak 与 __block的区别理解
- Unexpected Exception caught setting 'upfile' on 'class yuhao.action.FileAction: Error setting expres
- iOS-Core-Animation-Advanced-Techniques(七-2)
- 正则表达式基础1
- Move Zeroes
- 第2章 符号
- __weak与__block区别
- GRETA
- iOS-Core-Animation-Advanced-Techniques(七-3)
- nine-patch 转载制作方式http://2528.iteye.com/blog/1326647
- Insert键与vs黑标
- GDB下查看内存命令(x命令)
- win7+vm8.0+ubunt14.04+up6410 搭建嵌入式开发环境
- Android shrinkResources true 引发的血案
- Java NIO系列教程