標(biāo)題確實(shí)很長,有點(diǎn)長……??。
不理解標(biāo)題意思沒問題,看看下面的代碼就知道這次我想復(fù)習(xí)的地方是什么了。
UIButton *testButton = [[UIButton alloc] init];
[self.view addSubview:testButton];
[testButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@100);
make.height.equalTo(@100);
make.left.equalTo(self.view.mas_left);
make.top.equalTo(self.view.mas_top);
}];
上面的 block 里面使用了self,這樣子不就是循環(huán)引用了嗎?真的retain circle了嗎?。。。然后一陣沉默。。。。
其實(shí)面對突如其來的問題,即使不能馬上回答,或者真的被別人鄙視了也應(yīng)該抱著無所謂的的態(tài)度(其實(shí)心里已經(jīng)一百萬只cnm在奔騰了。。。??),接下來耗一點(diǎn)點(diǎn)的娛樂時間,把問題從自己理解的角度去思考為什么且一步一步解答下來才能真正將該知識點(diǎn)吃進(jìn)自己的心里。時至今日,答案這類生物在互聯(lián)網(wǎng)上還是能夠很好地捕捉的到,還沒到“拼爹”的地步呢~
進(jìn)入正題前……,上述代碼不會出現(xiàn) retain circle。
一、什么是 retain circle?
retain cycle表示兩個對象之間互相強(qiáng)引用/互相retain對方的情況,導(dǎo)致對象之間誰也釋放不了,造成內(nèi)存泄露。
從reference count 方面解析,即當(dāng)兩個對象互相強(qiáng)引用的時候,雙方的引用計(jì)數(shù)都是+1的,導(dǎo)致任何時候引用計(jì)數(shù)都不為0,始終無法釋放,無法釋放他們的內(nèi)存,即使已經(jīng)沒有變量持有他們。
二、打破 retain circle
其中最常用的方法是:
- 使用 weak 弱引用修飾屬性
- 使用__weak修飾 self
- 在 block 調(diào)用之后將 block 置 nil。
(以上情況之后有空再補(bǔ)充說明,相信有經(jīng)驗(yàn)的你可以大概配對到對應(yīng)的使用場景了??)
三、block 中引用 self 要注意循環(huán)引用問題
很多時候,我們遇到 block 第一反應(yīng)都會問,是否需要使用 weakSelf 防止循環(huán)引用,這也是蘋果文檔Avoid Strong Reference Cycles when Capturing self提醒的問題之一。

從以上可以知道,block 會捕獲外部變量,并且強(qiáng)引用它。上述例子也是一個經(jīng)典的 block 循環(huán)引用例子。那讓我們分析一下文章開頭的代碼:
UIButton *testButton = [[UIButton alloc] init];
[self.view addSubview:testButton];
[testButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(@100);
make.height.equalTo(@100);
make.left.equalTo(self.view.mas_left);
make.top.equalTo(self.view.mas_top);
}];
- 控制器 self 會強(qiáng)引用自己的子類 view。self ---> view
- 因?yàn)閠estButton實(shí)例被添加都 view 內(nèi),同樣view會強(qiáng)引用testButton。view ---> testButton
- testButton調(diào)用了 masonry 的 mas_makeConstraints方法,其中參數(shù) block 中引用了 self,問題來了,這里的 block 對 self 是強(qiáng)引用嗎?答案是,是的,請留意上面蘋果文檔中提過
locks maintain strong references to any captured objects, including self。那就是說這樣使用是會循環(huán)引用嗎? - 其實(shí),面對第3步的分析,我們還有一個點(diǎn)給忽略了,此刻我們也可以參照上面蘋果文檔中舉的經(jīng)典例子,該例子中開始的條件是,self copy 了 block 這個屬性,它的循環(huán)鏈?zhǔn)牵簊elf ---> block ---> self。
那好,我們回到第3步來,確實(shí),mas_makeConstraints方法,其中參數(shù) block 中強(qiáng)引用了 self,但 testButton 對 block 有強(qiáng)引用嗎?現(xiàn)在的循環(huán)鏈情況是:self ---> view ---> testButton ?block ---> self;問號是強(qiáng)還是弱呢? - 要想知道第4步的答案,得進(jìn)入mas_makeConstraints方法里看看它的實(shí)現(xiàn)方式才行:
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
這里的block是局部變量, 也就是說當(dāng) block 超出了方法的作用域時就會被銷毀,相當(dāng)于前面的第二標(biāo)題打破 retain circle 中最后描述的方法在 block 調(diào)用之后將 block 置 nil一樣。所以,上述的的循環(huán)鏈應(yīng)該是:self ---> view ---> testButton --x--> block ---> self;并無造成循環(huán)引用。
當(dāng)然,使用第三庫時候有任何問題都應(yīng)該先通過 issue 來排查一下,看看是否早已有人和你一樣遇到同樣的問題~
總結(jié)
綜上所述,在使用 masonry 時調(diào)用方法 mas_makeConstraints 中可以不使用 weakSelf。
留給自己的作業(yè):
- copy 與 strong 有啥區(qū)別?都是強(qiáng)引用嗎?
- strong weak dance 用在哪里?怎樣的 weak strong 定義最好?
Ref
- 常見循環(huán)引用問題產(chǎn)生點(diǎn):iOS 中的循環(huán)引用
- 從堆棧角度了解循環(huán)引用的產(chǎn)生原因:循環(huán)引用,看我就對了
- Programming with Objective-C
- MRC下 block 的 retain circle:正確使用Block避免Cycle Retain和Crash
- block 中捕獲的實(shí)例變量問題:Block 中何時可以直接用 self,何時必須用 weakSelf / strongSelf?
- 帶圖說明循環(huán)引用的:iOS中block的循環(huán)引用問題
