iOS ? block循環(huán)引用

請耐心把這篇文章看完,你對 Block 會(huì)有更深刻的了解。

這里直接用一個(gè)需求來探究循環(huán)引用的問題:如果我想在Block中延時(shí)來運(yùn)行某段代碼,這里就會(huì)出現(xiàn)一個(gè)問題,看這段代碼:

- (void)viewDidLoad {

?[super viewDidLoad];

?MitPerson*person = [[MitPerson alloc]init];

?__weak MitPerson * weakPerson = person;

?person.mitBlock = ^{

? ? ?dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

? ? ? ? ?[weakPerson test];

? ? ?});

?};

?person.mitBlock();

}

直接運(yùn)行這段代碼會(huì)發(fā)現(xiàn)[weakPerson test];并沒有執(zhí)行,打印一下會(huì)發(fā)現(xiàn),weakPerson 已經(jīng)是 Nil 了,這是由于當(dāng)我們的 viewDidLoad 方法運(yùn)行結(jié)束,由于是局部變量,無論是 MitPerson 和 weakPerson 都會(huì)被釋放掉,那么這個(gè)時(shí)候在 Block 中就無法拿到正真的 person 內(nèi)容了。

按如下方法修改代碼:

- (void)viewDidLoad {

?[super viewDidLoad];

?MitPerson*person = [[MitPerson alloc]init];

?__weak MitPerson * weakPerson = person;

?person.mitBlock = ^{

? ? ?__strong MitPerson * strongPerson = weakPerson;

? ? ?dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

? ? ? ? ?[strongPerson test];

? ? ?});

?};

?person.mitBlock();

}

這樣當(dāng)2秒過后,計(jì)時(shí)器依然能夠拿到想要的 person 對象。

二、深入探究原理

這里將會(huì)對每行代碼逐步進(jìn)行說明

1、開辟一段控件存儲(chǔ) person 類對象內(nèi)容,創(chuàng)建 person 強(qiáng)指針。

MitPerson*person = [[MitPerson alloc]init];

2、創(chuàng)建一個(gè)弱指針 weakPerson 指向person對象內(nèi)容

__weak MitPerson * weakPerson = person;

person.mitBlock = ^{

3、在 person 對象的 Block 內(nèi)部創(chuàng)建一個(gè)強(qiáng)指針來指向 person 對象,為了保證當(dāng)計(jì)時(shí)器執(zhí)行代碼的時(shí)候,person 對象沒有被系統(tǒng)銷毀所以我們必須在系統(tǒng)內(nèi)部進(jìn)行一次強(qiáng)引用,并用 GCD 計(jì)時(shí)器引用 strongPerson,為了保留 person 對象,在下面會(huì)對這里更加詳細(xì)的說明。

__strong MitPerson * strongPerson = weakPerson;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[strongPerson test];

});

};

4、執(zhí)行 Block 代碼

person.mitBlock();

下面將詳細(xì)分析一下下面這段代碼:

person.mitBlock = ^{

__strong MitPerson * strongPerson = weakPerson;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

[strongPerson test];

});

};

首先需要明白一些關(guān)于 Block 的概念:

1、默認(rèn)情況下,block 是放在棧里面的

2、一旦blcok進(jìn)行了copy操作,block的內(nèi)存就會(huì)被放在堆里面

3、堆立面的block(被copy過的block)有以下現(xiàn)象

1> block內(nèi)部如果通過外面聲明的強(qiáng)引用來使用,那么block內(nèi)部會(huì)自動(dòng)產(chǎn)生一個(gè)強(qiáng)引用指向所使用的對象。

2> block內(nèi)部如果通過外面聲明的弱引用來使用,那么block內(nèi)部會(huì)自動(dòng)產(chǎn)生一個(gè)弱引用指向所使用的對象。

我們進(jìn)行這段代碼的目的:

首先,我們需要在 Block 塊中調(diào)用,person 對象的方法,既然是在 Block 塊中我們就應(yīng)該使用弱指針來引用外部變量,以此來避免循環(huán)引用。但是又會(huì)出現(xiàn)問題,什么問題呢?就是當(dāng)我計(jì)時(shí)器要執(zhí)行方法的時(shí)候,發(fā)現(xiàn)對象已經(jīng)被釋放了。

接下來就是為了避免 person 對象在計(jì)時(shí)器執(zhí)行的時(shí)候被釋放掉:那么為什么 person 對象會(huì)被釋放掉呢?因?yàn)闊o論我們的person強(qiáng)指針還是 weakPerson 弱指針都是局部變量,當(dāng)執(zhí)行完ViewDidLoad 的時(shí)候,指針會(huì)被銷毀。對象只有被強(qiáng)指針引用的時(shí)候才不會(huì)被銷毀,而我們?nèi)绻苯右猛獠康膹?qiáng)指針對象又會(huì)產(chǎn)生循環(huán)引用,這個(gè)時(shí)候我們就用了一個(gè)巧妙的代碼來完成這個(gè)需求。

首先在 person.mitBlock 引用外部 weakPerson,并在內(nèi)部創(chuàng)建一個(gè)強(qiáng)指針去指向 person 對象,因?yàn)樵趦?nèi)部聲明變量,Block 是不會(huì)強(qiáng)引用這個(gè)對象的,這也就在避免的 person.mitBlock 循環(huán)引用風(fēng)險(xiǎn)的同時(shí),又創(chuàng)建出了一個(gè)強(qiáng)指針指向?qū)ο蟆?/p>

之后再用 GCD 延時(shí)器 Block 來引用相對于它來說是外部的變量 strongPerson ,這時(shí)延時(shí)器 Block 會(huì)默認(rèn)創(chuàng)建出來一個(gè)強(qiáng)引用來引用 person 對象,當(dāng) person.mitBlock 作用域結(jié)束之后 strongPerson 會(huì)跟著被銷毀,內(nèi)存中就僅剩下了 延時(shí)器 Block 強(qiáng)引用著 person 對象,2秒之后觸發(fā) test 方法,GCD Block 內(nèi)部方法執(zhí)行完畢之后,延時(shí)器和對象都被銷毀,這樣就完美實(shí)現(xiàn)了我們的需求。

最后再用一張圖來闡述各個(gè)指針、Block 與對象之間的關(guān)系

黑色代表強(qiáng)引用,綠色代表弱引用

總結(jié):person.mitBlock 中創(chuàng)建 strongPerson 是為了能夠使 GCD Block 保存 person 對象,創(chuàng)建 strongPerson 時(shí)候使用 weakPerson 是為了避免 mitBlock 直接引用外部強(qiáng)指針變量所造成的循環(huán)引用。

Block循環(huán)引用.png

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容