__weak與__block區(qū)別

首先我定義了一個類 MyObject 繼承 NSObject潮模,并添加了一個屬性 text,重寫了description方法篙贸,返回 text 的值君珠。這個主要是因為編譯器本身對 NSString 是有優(yōu)化的肾筐,創(chuàng)建的 string 對象有可能是靜態(tài)存儲區(qū)永不釋放的综膀,為了避免使用 NSString 引起一些問題,還是創(chuàng)建一個 NSObject 對象比較合適局齿。

另外我自定義了一個 TLog 方法輸出對象相關值,定義如下:

#define TLog(prefix,Obj) {NSLog(@"變量內(nèi)存地址:%p, 變量值:%p, 指向?qū)ο笾担?@, --> %@",&Obj,Obj,Obj,prefix);}



__weak

我們測試下面一段代碼

MyObject *obj = [[MyObject alloc]init];

obj.text = @"my-object";

TLog(@"obj", obj);

__weak MyObject *weakObj = obj;

TLog(@"weakObj", weakObj);

void(^testBlock)() = ^(){

? ? ?TLog(@"weakObj - block", weakObj);

};

testBlock();

obj = nil;

testBlock();

輸出:

變量內(nèi)存地址:0x7fff58c8a9f0, 變量值:0x7f8e0307f1d0, 指向?qū)ο笾担簃y-object, --> obj

變量內(nèi)存地址:0x7fff58c8a9e8, 變量值:0x7f8e0307f1d0, 指向?qū)ο笾担簃y-object, --> weakObj

變量內(nèi)存地址:0x7f8e030804c0, 變量值:0x7f8e0307f1d0, 指向?qū)ο笾担簃y-object, --> weakObj - block

變量內(nèi)存地址:0x7f8e030804c0, 變量值:0x0, 指向?qū)ο笾担?null), --> weakObj - block

從上面的結(jié)果可以看到

block 內(nèi)的 weakObj 和外部的 weakObj 并不是同一個變量

block 捕獲了 weakObj 同時也是對 obj 進行了弱引用橄登,當我在 block 外把 obj 釋放了之后抓歼,block 內(nèi)也讀不到這個變量了

當 obj 賦值 nil 時,block 內(nèi)部的 weakObj 也為 nil 了拢锹,也就是說 obj 實際上是被釋放了谣妻,可見 __weak 是可以避免循環(huán)引用問題的


接下來我們再看第二段代碼

MyObject *obj = [[MyObject alloc]init];

obj.text = @"my-object";

TLog(@"obj", obj);

__weak MyObject *weakObj = obj;

TLog(@"weakObj-0", weakObj);

void(^testBlock)() = ^(){

__strong MyObject *strongObj = weakObj;

TLog(@"weakObj - block", weakObj);

? ?TLog(@"strongObj - block", strongObj);

};

TLog(@"weakObj-1", weakObj);

testBlock();

TLog(@"weakObj-2", weakObj);

obj = nil;

testBlock();

TLog(@"weakObj-3", weakObj);

輸出

變量內(nèi)存地址:0x7fff5d7b2d18, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> obj

變量內(nèi)存地址:0x7fff5d7b2d10, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> weakObj-0

變量內(nèi)存地址:0x7fff5d7b2d10, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> weakObj-1

變量內(nèi)存地址:0x7fcf78f0f520, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> weakObj - block

變量內(nèi)存地址:0x7fff5d7b2bb8, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> strongObj - block

變量內(nèi)存地址:0x7fff5d7b2d10, 變量值:0x7fcf78c11e80, 指向?qū)ο笾担簃y-object, --> weakObj-2

變量內(nèi)存地址:0x7fcf78f0f520, 變量值:0x0, 指向?qū)ο笾担?null), --> weakObj - block

變量內(nèi)存地址:0x7fff5d7b2bb8, 變量值:0x0, 指向?qū)ο笾担?null), --> strongObj - block

變量內(nèi)存地址:0x7fff5d7b2d10, 變量值:0x0, 指向?qū)ο笾担?null), --> weakObj-3

如果你看過 AFNetworking 的源碼,會發(fā)現(xiàn) AFN 中作者會把變量在 block 外面先用 __weak 聲明卒稳,在 block 內(nèi)把前面 weak 聲明的變量賦值給 __strong 修飾的變量這種寫法蹋半。

從上面例子我們看到即使在 block 內(nèi)部用 strong 強引用了外面的 weakObj ,但是一旦 obj 釋放了之后充坑,內(nèi)部的 strongObj 同樣會變成 nil减江,那么這種寫法又有什么意義呢?

下面再看一段代碼:

MyObject *obj = [[MyObject alloc]init];

obj.text = @"my-object";

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;

TLog(@"weakObj - block", weakObj);

TLog(@"strongObj - block", strongObj);

sleep(3);

TLog(@"weakObj - block", weakObj);

TLog(@"strongObj - block", strongObj);

});

NSLog(@"------ sleep 1s");

sleep(1);

obj = nil;

TLog(@"weakObj-1", weakObj);

NSLog(@"------ sleep 5s");

sleep(5);

TLog(@"weakObj-2", weakObj);

執(zhí)行結(jié)果:

變量內(nèi)存地址:0x7fff58e2ad18, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> obj

變量內(nèi)存地址:0x7fff58e2ad10, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> weakObj-0

變量內(nèi)存地址:0x7fa2b1e80710, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> weakObj - block

變量內(nèi)存地址:0x700000093de8, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> strongObj - block

------ sleep 1s

變量內(nèi)存地址:0x7fff58e2ad10, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> weakObj-1

------ sleep 5s

變量內(nèi)存地址:0x7fa2b1e80710, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> weakObj - block

變量內(nèi)存地址:0x700000093de8, 變量值:0x7fa2b1e804e0, 指向?qū)ο笾担簃y-object, --> strongObj - block

變量內(nèi)存地址:0x7fff58e2ad10, 變量值:0x0, 指向?qū)ο笾担?null), --> weakObj-2

代碼中使用 sleep 來保證代碼執(zhí)行的先后順序捻爷。從結(jié)果中我們可以看到辈灼,只要 block 部分執(zhí)行了,即使我們中途釋放了 obj也榄,block 內(nèi)部依然會繼續(xù)強引用它巡莹。對比上面代碼,也就是說 block 內(nèi)部的 __strong 會在執(zhí)行期間進行強引用操作,保證在 block 內(nèi)部 strongObj 始終是可用的降宅。這種寫法非常巧妙骂远,既避免了循環(huán)引用的問題,又可以在 block 內(nèi)部持有該變量腰根。

綜合兩部分代碼激才,我們平時在使用時,常常先判斷 strongObj 是否為空唠雕,然后再執(zhí)行后續(xù)代碼贸营,如下方式:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

__strong MyObject *strongObj = weakObj;

if (strongObj) {

// do something ...

}

});

這種方式先判斷 Obj 是否被釋放,如果未釋放在執(zhí)行我們的代碼的時候保證其可用性岩睁。

__block

先上代碼

MyObject *obj = [[MyObject alloc]init];

obj.text = @"my-object-1";

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 = @"my-object-2";

TLog(@"obj2",obj2);

blockObj = obj2;

TLog(@"blockObj - block",blockObj);

};

NSLog(@"%@",testBlock);

TLog(@"blockObj -2",blockObj);

testBlock();

TLog(@"blockObj -3",blockObj);

結(jié)果

變量內(nèi)存地址:0x7fff5021a9f0, 變量值:0x7ff6b48d8cd0, 指向?qū)ο笾担簃y-object-1, --> obj

變量內(nèi)存地址:0x7fff5021a9e8, 變量值:0x7ff6b48d8cd0, 指向?qū)ο笾担簃y-object-1, --> blockObj -1

<__NSMallocBlock__: 0x7ff6b48d8c20>

變量內(nèi)存地址:0x7ff6b48da518, 變量值:0x7ff6b48d8cd0, 指向?qū)ο笾担簃y-object-1, --> blockObj -2

變量內(nèi)存地址:0x7ff6b48da518, 變量值:0x7ff6b48d8cd0, 指向?qū)ο笾担簃y-object-1, --> blockObj - block

變量內(nèi)存地址:0x7fff5021a7f8, 變量值:0x7ff6b48d9960, 指向?qū)ο笾担簃y-object-2, --> obj2

變量內(nèi)存地址:0x7ff6b48da518, 變量值:0x7ff6b48d9960, 指向?qū)ο笾担簃y-object-2, --> blockObj - block

變量內(nèi)存地址:0x7ff6b48da518, 變量值:0x7ff6b48d9960, 指向?qū)ο笾担簃y-object-2, --> blockObj -3

可以看到在 block 聲明前后 blockObj 的內(nèi)存地址是有所變化的钞脂,這涉及到 block 對外部變量的內(nèi)存管理問題,大家可以看擴展閱讀中的幾篇文章捕儒,對此有較深入的分析冰啃。

下面來看看 __block 能不能避免循環(huán)引用的問題

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);

輸出

變量內(nèi)存地址:0x7fff57eef9f0, 變量值:0x7ff86a55a160, 指向?qū)ο笾担?1111111111111, --> obj

變量內(nèi)存地址:0x7ff86c918a88, 變量值:0x7ff86a55a160, 指向?qū)ο笾担?1111111111111, --> blockObj - block

變量內(nèi)存地址:0x7ff86c918a88, 變量值:0x7ff86a55a160, 指向?qū)ο笾担?1111111111111, --> blockObj

當外部 obj 指向 nil 的時候,obj 理應被釋放刘莹,但實際上 blockObj 依然強引用著 obj阎毅,obj 其實并沒有被真正釋放。因此使用 __block 并不能避免循環(huán)引用的問題点弯。

但是我們可以通過手動釋放 blockObj 的方式來釋放 obj扇调,這就需要我們在 block 內(nèi)部將要退出的時候手動釋放掉 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 內(nèi)部能夠訪問到 obj抢肛,又可以避免循環(huán)引用的問題狼钮,但是這種方法也不是完美的,其存在下面幾個問題

必須記住在 block 底部釋放掉 block 變量捡絮,這其實跟 MRC 的形式有些類似了熬芜,不太適合 ARC

當在 block 外部修改了 blockObj 時,block 內(nèi)部的值也會改變福稳,反之在 block 內(nèi)部修改 blockObj 在外部再使用時值也會改變涎拉。這就需要在寫代碼時注意這個特性可能會帶來的一些隱患

__block 其實提升了變量的作用域,在 block 內(nèi)外訪問的都是同一個 blockObj 可能會造成一些隱患

總結(jié)5脑病9呐 !

__weak 本身是可以避免循環(huán)引用的問題的越妈,但是其會導致外部對象釋放了之后毁枯,block 內(nèi)部也訪問不到這個對象的問題,我們可以通過在 block 內(nèi)部聲明一個 __strong 的變量來指向 weakObj叮称,使外部對象既能在 block 內(nèi)部保持住种玛,又能避免循環(huán)引用的問題藐鹤。

__block 本身無法避免循環(huán)引用的問題,但是我們可以通過在 block 內(nèi)部手動把 blockObj 賦值為 nil 的方式來避免循環(huán)引用的問題赂韵。另外一點就是 __block 修飾的變量在 block 內(nèi)外都是唯一的娱节,要注意這個特性可能帶來的隱患。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末祭示,一起剝皮案震驚了整個濱河市肄满,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌质涛,老刑警劉巖稠歉,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異汇陆,居然都是意外死亡怒炸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門毡代,熙熙樓的掌柜王于貴愁眉苦臉地迎上來阅羹,“玉大人,你說我怎么就攤上這事教寂∧笥悖” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵酪耕,是天一觀的道長导梆。 經(jīng)常有香客問我,道長迂烁,這世上最難降的妖魔是什么看尼? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮婚被,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘梳虽。我一直安慰自己址芯,他們只是感情好,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布窜觉。 她就那樣靜靜地躺著谷炸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪禀挫。 梳的紋絲不亂的頭發(fā)上旬陡,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音语婴,去河邊找鬼描孟。 笑死驶睦,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的匿醒。 我是一名探鬼主播场航,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼廉羔!你這毒婦竟也來了溉痢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤憋他,失蹤者是張志新(化名)和其女友劉穎孩饼,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體竹挡,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡镀娶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了此迅。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汽畴。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖耸序,靈堂內(nèi)的尸體忽然破棺而出忍些,到底是詐尸還是另有隱情,我是刑警寧澤坎怪,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布罢坝,位于F島的核電站,受9級特大地震影響搅窿,放射性物質(zhì)發(fā)生泄漏嘁酿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一男应、第九天 我趴在偏房一處隱蔽的房頂上張望闹司。 院中可真熱鬧,春花似錦沐飘、人聲如沸游桩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽借卧。三九已至,卻和暖如春筛峭,著一層夾襖步出監(jiān)牢的瞬間铐刘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工影晓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镰吵,地道東北人檩禾。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像捡遍,于是被迫代替她去往敵國和親锌订。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容