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区别

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容