內(nèi)存泄露:
一個(gè)對(duì)象被其他對(duì)象強(qiáng)引用,在本來該作用域結(jié)束之后釋放調(diào)用(dealloc),但是沒有得到釋放(dealloc未執(zhí)行).
引起內(nèi)存泄露的常見原因有哪些?
參考這篇博文 : iOS內(nèi)存泄漏的常見情況,其中最常見的應(yīng)該是對(duì)象之間相互引用,block和代理了.
序言 :
- 宏
#define TLog(prefix,Obj) {NSLog(@"對(duì)象內(nèi)存地址:%p, 對(duì)象本身:%p, 指向?qū)ο螅?@, --> %@",&Obj,Obj,Obj,prefix);}
- 創(chuàng)建了兩個(gè)比較簡單的NSObject類,
#import "Obj_A.h"
#import "Obj_B.h"
- 以下代碼打印以^^^^^為整個(gè)方法的開始與結(jié)束.
具體查看 Obj A dealloc是否有調(diào)用?什么時(shí)候調(diào)用?
對(duì)象之間強(qiáng)引用
__weak和__strong的理解
驗(yàn)證方法一 :
- (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(@"這里面對(duì)象A應(yīng)用對(duì)象B,對(duì)象B引用對(duì)象A,兩者之間相互強(qiáng)應(yīng)用.所以會(huì)導(dǎo)致內(nèi)存泄露");
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
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] 這里面對(duì)象A應(yīng)用對(duì)象B,對(duì)象B引用對(duì)象A,兩者之間相互強(qiáng)應(yīng)用.所以會(huì)導(dǎo)致內(nèi)存泄露
2017-08-16 15:41:57.595 MemoryLeak[21241:695251] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
也就是方法結(jié)束之后,dealloc并沒有被執(zhí)行,也就是對(duì)象沒有被釋放
驗(yàn)證方法二 :
- (void)objProblem2 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
NSLog(@"__weak會(huì)不會(huì)改善內(nèi)存泄漏問題?");
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設(shè)置為nil");
weakObjA = nil;
TLog(@"obj_a", obj_a);
TLog(@"obj_b", obj_b);
TLog(@"weakObjA", weakObjA);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 15:43:54.705 MemoryLeak[21264:696548] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] __weak會(huì)不會(huì)改善內(nèi)存泄漏問題?
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9b8, 對(duì)象本身:0x60800003f4e0, 指向?qū)ο螅?lt;Obj_A: 0x60800003f4e0>, --> obj_a
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9b0, 對(duì)象本身:0x60800001f6c0, 指向?qū)ο螅?lt;Obj_B: 0x60800001f6c0>, --> obj_b
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9a8, 對(duì)象本身:0x60800003f4e0, 指向?qū)ο螅?lt;Obj_A: 0x60800003f4e0>, --> weakObjA
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] weakObjA設(shè)置為nil
2017-08-16 15:43:54.706 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9b8, 對(duì)象本身:0x60800003f4e0, 指向?qū)ο螅?lt;Obj_A: 0x60800003f4e0>, --> obj_a
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9b0, 對(duì)象本身:0x60800001f6c0, 指向?qū)ο螅?lt;Obj_B: 0x60800001f6c0>, --> obj_b
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] 對(duì)象內(nèi)存地址:0x7fff5ad8c9a8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> weakObjA
2017-08-16 15:43:54.707 MemoryLeak[21264:696548] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
這里參考了iOS開發(fā)中本人或同事碰到的內(nèi)存泄漏及解決辦法里面提到的截圖代碼.發(fā)現(xiàn)這樣子使用__weak的話對(duì)內(nèi)存泄露是沒有解決的.也用leaks檢查了好幾遍,同樣是發(fā)現(xiàn)有內(nèi)存泄露.
驗(yàn)證方法三 :
- (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設(shè)置為nil");
obj = nil;
TLog(@"obj", obj);
testBlock();
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 對(duì)象內(nèi)存地址:0x7fff53d6e9b8, 對(duì)象本身:0x6180000293c0, 指向?qū)ο螅?lt;Obj_A: 0x6180000293c0>, --> obj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 對(duì)象內(nèi)存地址:0x7fff53d6e9b0, 對(duì)象本身:0x6180000293c0, 指向?qū)ο螅?lt;Obj_A: 0x6180000293c0>, --> weakObj
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] 對(duì)象內(nèi)存地址:0x618000048ae0, 對(duì)象本身:0x6180000293c0, 指向?qū)ο螅?lt;Obj_A: 0x6180000293c0>, --> weakObj - block
2017-08-16 15:49:35.503 MemoryLeak[21319:699719] obj設(shè)置為nil
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] Obj A dealloc
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] 對(duì)象內(nèi)存地址:0x7fff53d6e9b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] 對(duì)象內(nèi)存地址:0x618000048ae0, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> weakObj - block
2017-08-16 15:49:35.504 MemoryLeak[21319:699719] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
總結(jié) :
- 當(dāng)obj設(shè)置為nil之后,weakObj在block中失去了引用的對(duì)象,所以不會(huì)導(dǎo)致內(nèi)存泄露.Obj調(diào)用了dealloc在方法結(jié)束之前釋放了對(duì)象.
- __weak注意使用場景,用于__block
驗(yàn)證方法四 :
__strong關(guān)鍵字
- (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設(shè)置為nil");
obj = nil;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
testBlock();
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 15:52:19.971 MemoryLeak[21368:701529] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:52:19.971 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d99b8, 對(duì)象本身:0x600000031b20, 指向?qū)ο螅?lt;Obj_A: 0x600000031b20>, --> obj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d99b0, 對(duì)象本身:0x600000031b20, 指向?qū)ο螅?lt;Obj_A: 0x600000031b20>, --> weakObj
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x600000240320, 對(duì)象本身:0x600000031b20, 指向?qū)ο螅?lt;Obj_A: 0x600000031b20>, --> weakObj - block
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d9898, 對(duì)象本身:0x600000031b20, 指向?qū)ο螅?lt;Obj_A: 0x600000031b20>, --> strongObj - block
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] obj設(shè)置為nil
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] Obj A dealloc
2017-08-16 15:52:19.972 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d99b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d99b0, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> weakObj
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x600000240320, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> weakObj - block
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] 對(duì)象內(nèi)存地址:0x7fff5e5d9898, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> strongObj - block
2017-08-16 15:52:19.973 MemoryLeak[21368:701529] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
總結(jié):
- 從上面例子我們看到即使在 block 內(nèi)部用 strong 強(qiáng)引用了外面的 weakObj ,但是一旦 obj 釋放了之后,
- 內(nèi)部的 strongObj 同樣會(huì)變成 nil,那么這種寫法又有什么意義呢,看驗(yàn)證方法五悲龟?
驗(yàn)證方法五 :
- (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設(shè)置為nil");
obj = nil;
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
NSLog(@"*************");
sleep(10);
NSLog(@"After 10s");
TLog(@"obj", obj);
TLog(@"weakObj", weakObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] 對(duì)象內(nèi)存地址:0x7fff5bed69b8, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;Obj_A: 0x610000023c00>, --> obj
2017-08-16 15:55:48.772 MemoryLeak[21408:703939] 對(duì)象內(nèi)存地址:0x7fff5bed69b0, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;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] 對(duì)象內(nèi)存地址:0x6000000535e0, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;Obj_A: 0x610000023c00>, --> weakObj - block
2017-08-16 15:55:48.772 MemoryLeak[21408:704010] 對(duì)象內(nèi)存地址:0x700002cc6d88, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;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設(shè)置為nil
2017-08-16 15:55:49.773 MemoryLeak[21408:703939] 對(duì)象內(nèi)存地址:0x7fff5bed69b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 15:55:49.773 MemoryLeak[21408:703939] 對(duì)象內(nèi)存地址:0x7fff5bed69b0, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;Obj_A: 0x610000023c00>, --> weakObj
2017-08-16 15:55:49.774 MemoryLeak[21408:703939] *************
2017-08-16 15:55:53.777 MemoryLeak[21408:704010] 對(duì)象內(nèi)存地址:0x6000000535e0, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;Obj_A: 0x610000023c00>, --> weakObj - block
2017-08-16 15:55:53.778 MemoryLeak[21408:704010] 對(duì)象內(nèi)存地址:0x700002cc6d88, 對(duì)象本身:0x610000023c00, 指向?qū)ο螅?lt;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] 對(duì)象內(nèi)存地址:0x7fff5bed69b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 15:55:59.775 MemoryLeak[21408:703939] 對(duì)象內(nèi)存地址:0x7fff5bed69b0, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> weakObj
2017-08-16 15:55:59.775 MemoryLeak[21408:703939] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
總結(jié) :
- 代碼中使用 sleep 來保證代碼執(zhí)行的先后順序。從結(jié)果中我們可以看到通今,只要 block 部分執(zhí)行了沛厨,即使我們中途釋放了 obj系馆,block 內(nèi)部依然會(huì)繼續(xù)強(qiáng)引用它痴突。
- 對(duì)比上面代碼搂蜓,也就是說 block 內(nèi)部的 __strong 會(huì)在執(zhí)行期間進(jìn)行強(qiáng)引用操作,保證在 block 內(nèi)部 strongObj 始終是可用的辽装。
- 這種寫法非常巧妙洛勉,既避免了循環(huán)引用的問題,又可以在 block 內(nèi)部持有該變量如迟。
對(duì)象之間強(qiáng)引用
__block的理解
驗(yàn)證方法一:
- (void)blockProblem1 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj設(shè)置為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不再應(yīng)用obj,那么obj則釋放");
blockObj = obj2;
TLog(@"blockObj - block",blockObj);
NSLog(@"***********結(jié)束block***********");
};
NSLog(@"設(shè)置block內(nèi)容之后");
TLog(@"blockObj\n--------------------",blockObj);
NSLog(@"運(yùn)行block");
testBlock();
TLog(@"blockObj\n--------------------",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 16:04:58.457 MemoryLeak[21496:708962] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x7fff555dc9b8, 對(duì)象本身:0x600000022c20, 指向?qū)ο螅?lt;Obj_A: 0x600000022c20>, --> obj
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x7fff555dc9b0, 對(duì)象本身:0x600000022c20, 指向?qū)ο螅?lt;Obj_A: 0x600000022c20>, --> blockObj
----------
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] obj設(shè)置為nil
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x7fff555dc9b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 16:04:58.458 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x7fff555dc9b0, 對(duì)象本身:0x600000022c20, 指向?qū)ο螅?lt;Obj_A: 0x600000022c20>, --> blockObj
----------
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 設(shè)置block內(nèi)容之后
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x61000004a408, 對(duì)象本身:0x600000022c20, 指向?qū)ο螅?lt;Obj_A: 0x600000022c20>, --> blockObj
--------------------
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 運(yùn)行block
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] ***********開始block***********
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x61000004a408, 對(duì)象本身:0x600000022c20, 指向?qū)ο螅?lt;Obj_A: 0x600000022c20>, --> blockObj - block
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x7fff555dc8e8, 對(duì)象本身:0x608000022a00, 指向?qū)ο螅?lt;Obj_A: 0x608000022a00>, --> obj2
2017-08-16 16:04:58.459 MemoryLeak[21496:708962] 將blockObj = obj2,只要blockObj不再應(yīng)用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] 對(duì)象內(nèi)存地址:0x61000004a408, 對(duì)象本身:0x608000022a00, 指向?qū)ο螅?lt;Obj_A: 0x608000022a00>, --> blockObj - block
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] ***********結(jié)束block***********
2017-08-16 16:04:58.460 MemoryLeak[21496:708962] 對(duì)象內(nèi)存地址:0x61000004a408, 對(duì)象本身:0x608000022a00, 指向?qū)ο螅?lt;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
總結(jié) :
- 可以看到在 block 聲明前后 blockObj 的內(nèi)存地址是有所變化的,這涉及到 block 對(duì)外部變量的內(nèi)存管理問題,大家可以看擴(kuò)展閱讀中的幾篇文章殷勘,對(duì)此有較深入的分析此再。
- 這里可以注意到,雖然沒有執(zhí)行block,但是里面設(shè)置blockObj之后,已經(jīng)對(duì)blockObj進(jìn)行操作了,改變了其內(nèi)存地址.
驗(yàn)證方法二 :
- (void)blockProblem2 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj設(shè)置為nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"設(shè)置block內(nèi)容");
void(^testBlock)() = ^(){
TLog(@"blockObj - block",blockObj);
};
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"運(yùn)行block");
testBlock();
TLog(@"blockObj",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
總結(jié)
- 注意Obj A dealloc,是在整個(gè)方法結(jié)束之后.
- 當(dāng)外部 obj 指向 nil 的時(shí)候,obj 理應(yīng)被釋放玲销,但實(shí)際上 blockObj 依然強(qiáng)引用著 obj输拇,obj 其實(shí)并沒有被真正釋放。因此使用 __block 并不能避免循環(huán)引用的問題贤斜。
- 但是我們可以通過手動(dòng)釋放 blockObj 的方式來釋放 obj策吠,這就需要我們?cè)?block 內(nèi)部將要退出的時(shí)候手動(dòng)釋放掉 blockObj ,如下這種形式,驗(yàn)證方法三
驗(yàn)證方法三 :
- (void)blockProblem3 {
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
Obj_A *obj = [[Obj_A alloc]init];
__block Obj_A *blockObj = obj;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"obj設(shè)置為nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"設(shè)置block內(nèi)容");
void(^testBlock)() = ^(){
TLog(@"blockObj - block",blockObj);
NSLog(@"blockObj設(shè)置為nil");
blockObj = nil;
};
NSLog(@"obj設(shè)置為nil");
obj = nil;
TLog(@"obj",obj);
TLog(@"blockObj\n----------",blockObj);
NSLog(@"運(yùn)行block");
testBlock();
TLog(@"blockObj",blockObj);
NSLog(@"^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
}
打印結(jié)果
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x7fff5e9709b8, 對(duì)象本身:0x600000039560, 指向?qū)ο螅?lt;Obj_A: 0x600000039560>, --> obj
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x7fff5e9709b0, 對(duì)象本身:0x600000039560, 指向?qū)ο螅?lt;Obj_A: 0x600000039560>, --> blockObj
----------
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] obj設(shè)置為nil
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x7fff5e9709b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 16:09:30.042 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x7fff5e9709b0, 對(duì)象本身:0x600000039560, 指向?qū)ο螅?lt;Obj_A: 0x600000039560>, --> blockObj
----------
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 設(shè)置block內(nèi)容
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] obj設(shè)置為nil
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x7fff5e9709b8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> obj
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x608000050ee8, 對(duì)象本身:0x600000039560, 指向?qū)ο螅?lt;Obj_A: 0x600000039560>, --> blockObj
----------
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 運(yùn)行block
2017-08-16 16:09:30.043 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x608000050ee8, 對(duì)象本身:0x600000039560, 指向?qū)ο螅?lt;Obj_A: 0x600000039560>, --> blockObj - block
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] blockObj設(shè)置為nil
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] Obj A dealloc
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] 對(duì)象內(nèi)存地址:0x608000050ee8, 對(duì)象本身:0x0, 指向?qū)ο螅?null), --> blockObj
2017-08-16 16:09:30.044 MemoryLeak[21537:711747] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
總結(jié)
- 這里也就是提前手動(dòng)釋放block引用的對(duì)象,這種形式既能保證在 block 內(nèi)部能夠訪問到 obj瘩绒,又可以避免循環(huán)引用的問題猴抹,但是這種方法也不是完美的,其存在下面幾個(gè)問題
- 必須記住在 block 底部釋放掉 block 變量锁荔,這其實(shí)跟 MRC 的形式有些類似了蟀给,不太適合 ARC
- 當(dāng)在 block 外部修改了 blockObj 時(shí),block 內(nèi)部的值也會(huì)改變阳堕,反之在 block 內(nèi)部修改 blockObj 在外部再使用時(shí)值也會(huì)改變跋理。
- 這就需要在寫代碼時(shí)注意這個(gè)特性可能會(huì)帶來的一些隱患 ,__block 其實(shí)提升了變量的作用域,在 block 內(nèi)外訪問的都是同一個(gè) blockObj 可能會(huì)造成一些隱患
關(guān)于__weak和__strong的總結(jié)
總結(jié)
__weak 本身是可以避免循環(huán)引用的問題的恬总,但是其會(huì)導(dǎo)致外部對(duì)象釋放了之后前普,block 內(nèi)部也訪問不到這個(gè)對(duì)象的問題,我們可以通過在 block 內(nèi)部聲明一個(gè) __strong 的變量來指向 weakObj壹堰,使外部對(duì)象既能在 block 內(nèi)部保持住拭卿,又能避免循環(huán)引用的問題。
__block 本身無法避免循環(huán)引用的問題缀旁,但是我們可以通過在 block 內(nèi)部手動(dòng)把 blockObj 賦值為 nil 的方式來避免循環(huán)引用的問題记劈。另外一點(diǎn)就是 __block 修飾的變量在 block 內(nèi)外都是唯一的,要注意這個(gè)特性可能帶來的隱患并巍。
參考博文 :
block教程系列
__weak與__block區(qū)別