iOS内存泄露&&检测

内存泄露:

一个对象被其他对象强引用,在本来该作用域结束之后释放调用(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: 0x60800003f4e0>, --> obj_a
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b0, 对象本身:0x60800001f6c0, 指向对象:<Obj_B: 0x60800001f6c0>, --> obj_b
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9a8, 对象本身:0x60800003f4e0, 指向对象:<Obj_A: 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: 0x60800003f4e0>, --> obj_a
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] 对象内存地址:0x7fff5ad8c9b0, 对象本身:0x60800001f6c0, 指向对象:<Obj_B: 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_A: 0x6180000293c0>, --> obj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 对象内存地址:0x7fff53d6e9b0, 对象本身:0x6180000293c0, 指向对象:<Obj_A: 0x6180000293c0>, --> weakObj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 对象内存地址:0x618000048ae0, 对象本身:0x6180000293c0, 指向对象:<Obj_A: 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_A: 0x600000031b20>, --> obj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d99b0, 对象本身:0x600000031b20, 指向对象:<Obj_A: 0x600000031b20>, --> weakObj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x600000240320, 对象本身:0x600000031b20, 指向对象:<Obj_A: 0x600000031b20>, --> weakObj - block
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 对象内存地址:0x7fff5e5d9898, 对象本身:0x600000031b20, 指向对象:<Obj_A: 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_A: 0x610000023c00>, --> obj
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] 对象内存地址:0x7fff5bed69b0, 对象本身:0x610000023c00, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 0x610000023c00>, --> weakObj - block
2017-08-16 15:55:48.772 MemoryLeak[21408:704010] 对象内存地址:0x700002cc6d88, 对象本身:0x610000023c00, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 0x610000023c00>, --> weakObj
2017-08-16 15:55:49.774 MemoryLeak[21408:703939] *************
2017-08-16 15:55:53.777 MemoryLeak[21408:704010] 对象内存地址:0x6000000535e0, 对象本身:0x610000023c00, 指向对象:<Obj_A: 0x610000023c00>, --> weakObj - block
2017-08-16 15:55:53.778 MemoryLeak[21408:704010] 对象内存地址:0x700002cc6d88, 对象本身:0x610000023c00, 指向对象:<Obj_A: 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_A: 0x600000022c20>, --> obj
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc9b0, 对象本身:0x600000022c20, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 0x600000022c20>, --> blockObj - block
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 对象内存地址:0x7fff555dc8e8, 对象本身:0x608000022a00, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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_A: 0x600000039560>, --> obj
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 对象内存地址:0x7fff5e9709b0, 对象本身:0x600000039560, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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, 指向对象:<Obj_A: 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,又可以避免循环引用的问题,但是这种方法也不是完美的,其存在下面几个问题
      1. 必须记住在 block 底部释放掉 block 变量,这其实跟 MRC 的形式有些类似了,不太适合 ARC
      1. 当在 block 外部修改了 blockObj 时,block 内部的值也会改变,反之在 block 内部修改 blockObj 在外部再使用时值也会改变。
      1. 这就需要在写代码时注意这个特性可能会带来的一些隐患 ,__block 其实提升了变量的作用域,在 block 内外访问的都是同一个 blockObj 可能会造成一些隐患

关于__weak和__strong的总结

总结

  • __weak 本身是可以避免循环引用的问题的,但是其会导致外部对象释放了之后,block 内部也访问不到这个对象的问题,我们可以通过在 block 内部声明一个 __strong 的变量来指向 weakObj,使外部对象既能在 block 内部保持住,又能避免循环引用的问题。

  • __block 本身无法避免循环引用的问题,但是我们可以通过在 block 内部手动把 blockObj 赋值为 nil 的方式来避免循环引用的问题。另外一点就是 __block 修饰的变量在 block 内外都是唯一的,要注意这个特性可能带来的隐患。


参考博文 :
block教程系列
__weak与__block区别

推荐阅读更多精彩内容