作者:Mitchell
一、根據(jù)需求提出問題
- 請(qǐng)耐心把這篇文章看完,你對(duì) 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 對(duì)象。
二、深入探究原理
- 這里將會(huì)對(duì)每行代碼逐步進(jìn)行說明
1、開辟一段控件存儲(chǔ) person 類對(duì)象內(nèi)容,創(chuàng)建 person 強(qiáng)指針。
MitPerson*person = [[MitPerson alloc]init];
2、創(chuàng)建一個(gè)弱指針 weakPerson 指向person對(duì)象內(nèi)容
__weak MitPerson * weakPerson = person;
person.mitBlock = ^{
3、在 person 對(duì)象的 Block 內(nèi)部創(chuàng)建一個(gè)強(qiáng)指針來指向 person 對(duì)象,為了保證當(dāng)計(jì)時(shí)器執(zhí)行代碼的時(shí)候,person 對(duì)象沒有被系統(tǒng)銷毀所以我們必須在系統(tǒng)內(nèi)部進(jìn)行一次強(qiáng)引用,并用 GCD 計(jì)時(shí)器引用 strongPerson,為了保留 person 對(duì)象,在下面會(huì)對(duì)這里更加詳細(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)引用指向所使用的對(duì)象。
- 2> block內(nèi)部如果通過外面聲明的弱引用來使用,那么block內(nèi)部會(huì)自動(dòng)產(chǎn)生一個(gè)弱引用指向所使用的對(duì)象。
- 我們進(jìn)行這段代碼的目的:
- 首先,我們需要在 Block 塊中調(diào)用,person 對(duì)象的方法,既然是在 Block 塊中我們就應(yīng)該使用弱指針來引用外部變量,以此來避免循環(huán)引用。但是又會(huì)出現(xiàn)問題,什么問題呢?就是當(dāng)我計(jì)時(shí)器要執(zhí)行方法的時(shí)候,發(fā)現(xiàn)對(duì)象已經(jīng)被釋放了。
- 接下來就是為了避免 person 對(duì)象在計(jì)時(shí)器執(zhí)行的時(shí)候被釋放掉:那么為什么 person 對(duì)象會(huì)被釋放掉呢?因?yàn)闊o論我們的person強(qiáng)指針還是 weakPerson 弱指針都是局部變量,當(dāng)執(zhí)行完ViewDidLoad 的時(shí)候,指針會(huì)被銷毀。對(duì)象只有被強(qiáng)指針引用的時(shí)候才不會(huì)被銷毀,而我們?nèi)绻苯右猛獠康膹?qiáng)指針對(duì)象又會(huì)產(chǎn)生循環(huán)引用,這個(gè)時(shí)候我們就用了一個(gè)巧妙的代碼來完成這個(gè)需求。
- 首先在 person.mitBlock 引用外部 weakPerson,并在內(nèi)部創(chuàng)建一個(gè)強(qiáng)指針去指向 person 對(duì)象,因?yàn)樵趦?nèi)部聲明變量,Block 是不會(huì)強(qiáng)引用這個(gè)對(duì)象的,這也就在避免的 person.mitBlock 循環(huán)引用風(fēng)險(xiǎn)的同時(shí),又創(chuàng)建出了一個(gè)強(qiáng)指針指向?qū)ο蟆?/li>
- 之后再用 GCD 延時(shí)器 Block 來引用相對(duì)于它來說是外部的變量 strongPerson ,這時(shí)延時(shí)器 Block 會(huì)默認(rèn)創(chuàng)建出來一個(gè)強(qiáng)引用來引用 person 對(duì)象,當(dāng) person.mitBlock 作用域結(jié)束之后 strongPerson 會(huì)跟著被銷毀,內(nèi)存中就僅剩下了 延時(shí)器 Block 強(qiáng)引用著 person 對(duì)象,2秒之后觸發(fā) test 方法,GCD Block 內(nèi)部方法執(zhí)行完畢之后,延時(shí)器和對(duì)象都被銷毀,這樣就完美實(shí)現(xiàn)了我們的需求。
- 最后再用一張圖來闡述各個(gè)指針、Block 與對(duì)象之間的關(guān)系
黑色代表強(qiáng)引用,綠色代表弱引用-
總結(jié):person.mitBlock 中創(chuàng)建 strongPerson 是為了能夠使 GCD Block 保存 person 對(duì)象,創(chuàng)建 strongPerson 時(shí)候使用 weakPerson 是為了避免 mitBlock 直接引用外部強(qiáng)指針變量所造成的循環(huán)引用。
Block循環(huán)引用.png
-
總結(jié):person.mitBlock 中創(chuàng)建 strongPerson 是為了能夠使 GCD Block 保存 person 對(duì)象,創(chuàng)建 strongPerson 時(shí)候使用 weakPerson 是為了避免 mitBlock 直接引用外部強(qiáng)指針變量所造成的循環(huán)引用。
