内存泄露:
一个对象被其他对象强引用,在本来该作用域结束之后释放调用(dealloc),但是没有得到释放(dealloc未执行).
引起内存泄露的常见原因有哪些?
参考这篇博文 : iOS内存泄漏的常见情况,其中最常见的应该是对象之间相互引用,block和代理了.
序言 :
- 宏
#define TLog(prefix,Obj) {NSLog(@"对象内存地址:%p, 对象本身:%p, 指向对象:%@, --> %@",&Obj,Obj,Obj,prefix);}
- 创建了两个比较简单的NSObject类,
#import "Obj_A.h"
#import "Obj_B.h"
- 以下代码打印以^^^^^为整个方法的开始与结束.
具体查看 Obj A dealloc是否有调用?什么时候调用?
对象之间强引用
__weak和__strong的理解
验证方法一 :
- (void)objProblem1 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj_a = [[Obj_A alloc]init];
Obj_B *obj_b = [[Obj_B alloc]init];
obj_a.obj = obj_b;
obj_b.obj = obj_a;
NSLog(@"最简单的例子");
NSLog(@"这里面对象A应用对象B,对象B引用对象A,两者之间相互强应用.所以会导致内存泄露");
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 15:41:57.594 MemoryLeak[21241:695251] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:41:57.595 MemoryLeak[21241:695251] 最简单的例子
2017-08-16 15:41:57.595 MemoryLeak[21241:695251] 这里面对象A应用对象B,对象B引用对象A,两者之间相互强应用.所以会导致内存泄露
2017-08-16 15:41:57.595 MemoryLeak[21241:695251] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
也就是方法结束之后,dealloc并没有被执行,也就是对象没有被释放
验证方法二 :
- (void)objProblem2 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
NSLog(@"__weak会不会改善内存泄漏问题?");
Obj_A *obj_a = [[Obj_A alloc]init];
Obj_B *obj_b = [[Obj_B alloc]init];
__weak typeof(obj_a) weakObjA = obj_a;
TLog(@"obj_a", obj_a);
TLog(@"obj_b", obj_b);
TLog(@"weakObjA", weakObjA);
weakObjA.obj = obj_b;
obj_b.obj = weakObjA;
NSLog(@"weakObjA设置为nil");
weakObjA = nil;
TLog(@"obj_a", obj_a);
TLog(@"obj_b", obj_b);
TLog(@"weakObjA", weakObjA);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 15:43:54.705 MemoryLeak[21264:696548] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] __weak会不会改善内存泄漏问题?
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b8, 对象本身:0x60800003f4e0, 指向对象:, --> obj_a
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b0, 对象本身:0x60800001f6c0, 指向对象:, --> obj_b
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9a8, 对象本身:0x60800003f4e0, 指向对象:, --> weakObjA
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] weakObjA设置为nil
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b8, 对象本身:0x60800003f4e0, 指向对象:, --> obj_a
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b0, 对象本身:0x60800001f6c0, 指向对象:, --> obj_b
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9a8, 对象本身:0x0, 指向对象:(null), --> weakObjA
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
这里参考了iOS开发中本人或同事碰到的内存泄漏及解决办法里面提到的截图代码.发现这样子使用__weak的话对内存泄露是没有解决的.也用leaks检查了好几遍,同样是发现有内存泄露.
验证方法三 :
- (void)objProblem3 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__weak Obj_A *weakObj = obj;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
void(^testBlock)() = ^(){
TLog(@"weakObj - block", weakObj);
};
testBlock();
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj", obj);
testBlock();
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 对象内存地址:0x7fff53d6e9b8, 对象本身:0x6180000293c0, 指向对象:, --> obj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 对象内存地址:0x7fff53d6e9b0, 对象本身:0x6180000293c0, 指向对象:, --> weakObj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 对象内存地址:0x618000048ae0, 对象本身:0x6180000293c0, 指向对象:, --> weakObj - block
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] obj设置为nil
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] Obj A dealloc
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] 对象内存地址:0x7fff53d6e9b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] 对象内存地址:0x618000048ae0, 对象本身:0x0, 指向对象:(null), --> weakObj - block
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
总结 :
- 当obj设置为nil之后,weakObj在block中失去了引用的对象,所以不会导致内存泄露.Obj调用了dealloc在方法结束之前释放了对象.
- __weak注意使用场景,用于__block
验证方法四 :
__strong关键字
- (void)objProblem4 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__weak Obj_A *weakObj = obj;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
void(^testBlock)() = ^(){
__strong Obj_A *strongObj = weakObj;
TLog(@"weakObj - block", weakObj);
TLog(@"strongObj - block", strongObj);
};
testBlock();
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
testBlock();
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 15:52:19.971 MemoryLeak[21368:701529] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:52:19.971 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d99b8, 对象本身:0x600000031b20, 指向对象:, --> obj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d99b0, 对象本身:0x600000031b20, 指向对象:, --> weakObj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x600000240320, 对象本身:0x600000031b20, 指向对象:, --> weakObj - block
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d9898, 对象本身:0x600000031b20, 指向对象:, --> strongObj - block
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] obj设置为nil
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] Obj A dealloc
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d99b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d99b0, 对象本身:0x0, 指向对象:(null), --> weakObj
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 对象内存地址:0x600000240320, 对象本身:0x0, 指向对象:(null), --> weakObj - block
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d9898, 对象本身:0x0, 指向对象:(null), --> strongObj - block
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
总结:
- 从上面例子我们看到即使在 block 内部用 strong 强引用了外面的 weakObj ,但是一旦 obj 释放了之后,
- 内部的 strongObj 同样会变成 nil,那么这种写法又有什么意义呢,看验证方法五?
验证方法五 :
- (void)objProblem5 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__weak Obj_A *weakObj = obj;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"start---");
__strong Obj_A *strongObj = weakObj;
TLog(@"weakObj - block", weakObj);
TLog(@"strongObj - block", strongObj);
sleep(5);
TLog(@"weakObj - block", weakObj);
TLog(@"strongObj - block", strongObj);
NSLog(@"end----");
});
sleep(1);
NSLog(@"*************");
NSLog(@"After 1s obj设置为nil");
obj = nil;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
NSLog(@"*************");
sleep(10);
NSLog(@"After 10s");
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b8, 对象本身:0x610000023c00, 指向对象:, --> obj
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b0, 对象本身:0x610000023c00, 指向对象:, --> weakObj
2017-08-16 15:55:48.772 MemoryLeak[21408:704010] start---
2017-08-16 15:55:48.772 MemoryLeak[21408:704010] 对象内存地址:0x6000000535e0, 对象本身:0x610000023c00, 指向对象:, --> weakObj - block
2017-08-16 15:55:48.772 MemoryLeak[21408:704010] 对象内存地址:0x700002cc6d88, 对象本身:0x610000023c00, 指向对象:, --> strongObj - block
2017-08-16 15:55:49.772 MemoryLeak[21408:703939] *************
2017-08-16 15:55:49.772 MemoryLeak[21408:703939] After 1s obj设置为nil
2017-08-16 15:55:49.773 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 15:55:49.773 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b0, 对象本身:0x610000023c00, 指向对象:, --> weakObj
2017-08-16 15:55:49.774 MemoryLeak[21408:703939] *************
2017-08-16 15:55:53.777 MemoryLeak[21408:704010] 对象内存地址:0x6000000535e0, 对象本身:0x610000023c00, 指向对象:, --> weakObj - block
2017-08-16 15:55:53.778 MemoryLeak[21408:704010] 对象内存地址:0x700002cc6d88, 对象本身:0x610000023c00, 指向对象:, --> strongObj - block
2017-08-16 15:55:53.778 MemoryLeak[21408:704010] end----
2017-08-16 15:55:53.778 MemoryLeak[21408:704010] Obj A dealloc
2017-08-16 15:55:59.774 MemoryLeak[21408:703939] After 10s
2017-08-16 15:55:59.775 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 15:55:59.775 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b0, 对象本身:0x0, 指向对象:(null), --> weakObj
2017-08-16 15:55:59.775 MemoryLeak[21408:703939] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
总结 :
- 代码中使用 sleep 来保证代码执行的先后顺序。从结果中我们可以看到,只要 block 部分执行了,即使我们中途释放了 obj,block 内部依然会继续强引用它。
- 对比上面代码,也就是说 block 内部的 __strong 会在执行期间进行强引用操作,保证在 block 内部 strongObj 始终是可用的。
- 这种写法非常巧妙,既避免了循环引用的问题,又可以在 block 内部持有该变量。
对象之间强引用
__block的理解
验证方法一:
- (void)blockProblem1 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
void(^testBlock)() = ^(){
NSLog(@"***********开始block***********");
TLog(@"blockObj - block",blockObj);
Obj_A *obj2 = [[Obj_A alloc]init];
TLog(@"obj2",obj2);
NSLog(@"将blockObj = obj2,只要blockObj不再应用obj,那么obj则释放");
blockObj = obj2;
TLog(@"blockObj - block",blockObj);
NSLog(@"***********结束block***********");
};
NSLog(@"设置block内容之后");
TLog(@"blockObj\n--------------------",blockObj);
NSLog(@"运行block");
testBlock();
TLog(@"blockObj\n--------------------",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 16:04:58.457 MemoryLeak[21496:708962] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc9b8, 对象本身:0x600000022c20, 指向对象:, --> obj
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc9b0, 对象本身:0x600000022c20, 指向对象:, --> blockObj
----------
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] obj设置为nil
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc9b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc9b0, 对象本身:0x600000022c20, 指向对象:, --> blockObj
----------
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 设置block内容之后
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 对象内存地址:0x61000004a408, 对象本身:0x600000022c20, 指向对象:, --> blockObj
--------------------
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 运行block
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] ***********开始block***********
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 对象内存地址:0x61000004a408, 对象本身:0x600000022c20, 指向对象:, --> blockObj - block
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc8e8, 对象本身:0x608000022a00, 指向对象:, --> obj2
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 将blockObj = obj2,只要blockObj不再应用obj,那么obj则释放
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] Obj A dealloc
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] 对象内存地址:0x61000004a408, 对象本身:0x608000022a00, 指向对象:, --> blockObj - block
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] ***********结束block***********
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] 对象内存地址:0x61000004a408, 对象本身:0x608000022a00, 指向对象:, --> blockObj
--------------------
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] Obj A dealloc
总结 :
- 可以看到在 block 声明前后 blockObj 的内存地址是有所变化的,这涉及到 block 对外部变量的内存管理问题,大家可以看扩展阅读中的几篇文章,对此有较深入的分析。
- 这里可以注意到,虽然没有执行block,但是里面设置blockObj之后,已经对blockObj进行操作了,改变了其内存地址.
验证方法二 :
- (void)blockProblem2 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"设置block内容");
void(^testBlock)() = ^(){
TLog(@"blockObj - block",blockObj);
};
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"运行block");
testBlock();
TLog(@"blockObj",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
总结
- 注意Obj A dealloc,是在整个方法结束之后.
- 当外部 obj 指向 nil 的时候,obj 理应被释放,但实际上 blockObj 依然强引用着 obj,obj 其实并没有被真正释放。因此使用 __block 并不能避免循环引用的问题。
- 但是我们可以通过手动释放 blockObj 的方式来释放 obj,这就需要我们在 block 内部将要退出的时候手动释放掉 blockObj ,如下这种形式,验证方法三
验证方法三 :
- (void)blockProblem3 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"设置block内容");
void(^testBlock)() = ^(){
TLog(@"blockObj - block",blockObj);
NSLog(@"blockObj设置为nil");
blockObj = nil;
};
NSLog(@"obj设置为nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"运行block");
testBlock();
TLog(@"blockObj",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印结果
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b8, 对象本身:0x600000039560, 指向对象:, --> obj
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b0, 对象本身:0x600000039560, 指向对象:, --> blockObj
----------
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] obj设置为nil
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b0, 对象本身:0x600000039560, 指向对象:, --> blockObj
----------
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 设置block内容
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] obj设置为nil
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b8, 对象本身:0x0, 指向对象:(null), --> obj
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 对象内存地址:0x608000050ee8, 对象本身:0x600000039560, 指向对象:, --> blockObj
----------
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 运行block
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 对象内存地址:0x608000050ee8, 对象本身:0x600000039560, 指向对象:, --> blockObj - block
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] blockObj设置为nil
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] Obj A dealloc
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] 对象内存地址:0x608000050ee8, 对象本身:0x0, 指向对象:(null), --> blockObj
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
总结
- 这里也就是提前手动释放block引用的对象,这种形式既能保证在 block 内部能够访问到 obj,又可以避免循环引用的问题,但是这种方法也不是完美的,其存在下面几个问题
-
- 必须记住在 block 底部释放掉 block 变量,这其实跟 MRC 的形式有些类似了,不太适合 ARC
-
- 当在 block 外部修改了 blockObj 时,block 内部的值也会改变,反之在 block 内部修改 blockObj 在外部再使用时值也会改变。
-
- 这就需要在写代码时注意这个特性可能会带来的一些隐患 ,__block 其实提升了变量的作用域,在 block 内外访问的都是同一个 blockObj 可能会造成一些隐患
-
关于__weak和__strong的总结
总结
__weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。
__block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。
参考博文 :
block教程系列
__weak与__block区别