結(jié)論:
雖然現(xiàn)在用weak
還是strong
并沒(méi)有什么實(shí)質(zhì)性差別袒哥,但是除非是有特定需要避免循環(huán)引用才用weak
,否則按照蘋(píng)果官方的觀(guān)點(diǎn)豆励,目前對(duì)于IBOutlet
的最佳實(shí)踐應(yīng)該是使用strong
起因:
最近用SwiftFormat工具來(lái)格式化swift代碼夺荒,有個(gè)現(xiàn)象引起了我的興趣:
用這個(gè)工具格式化的swift代碼了里面的IBOutlet
屬性里面的weak
修飾符都消失了,也就是它在格式化的時(shí)候把IBOutlet
屬性變成了用strong
修飾良蒸。
正巧最近新公司發(fā)了電腦技扼,新安裝了Xcode,發(fā)現(xiàn)新安裝的Xcode拖IBOutlet
屬性也是默認(rèn)選擇strong
嫩痰。
在之前的開(kāi)發(fā)中我一直以為IBOutlet
默認(rèn)就應(yīng)該是weak
剿吻,從沒(méi)考慮過(guò)strong
情況,但是這兩個(gè)肯定不是巧合串纺,其內(nèi)在應(yīng)該是有原因的丽旅。
研究:
其實(shí)這個(gè)問(wèn)題在StackOverFlow里早就有相關(guān)話(huà)題和討論結(jié)果:
Should IBOutlet be strong or weak under ARC?
大意就是:
蘋(píng)果建議除非是有特定需要避免循環(huán)引用才使用
weak
,否則目前對(duì)于IBOutlet
的最佳實(shí)踐應(yīng)該是使用strong
WWDC 2015的”Implementing UI Designs in Interface Builder”會(huì)議,則有個(gè)Apple的工程師說(shuō):
“通常你應(yīng)該用strong來(lái)修飾你的outlet,尤其是當(dāng)你要連接一個(gè)outlet到子視圖或者在視圖層次中不總是被持有的constraint(約束?)時(shí).只有當(dāng)你用自定義的視圖來(lái)引用一個(gè)東東,而這個(gè)東東反過(guò)來(lái)又引用了視圖層次時(shí)才需要用weak,但還是不推薦這么做.”
同時(shí)回答者在Twitter上詢(xún)問(wèn)了蘋(píng)果的IB團(tuán)隊(duì)的一個(gè)工程師確認(rèn)了這個(gè)事情纺棺。
那具體到用weak
和strong
到底有什么區(qū)別呢榄笙?
我們?cè)贗nterfaceBuilder里面往ViewController里拖一個(gè)label時(shí),這個(gè)label是加到了這個(gè)ViewController的view上祷蝌,view有一個(gè)subViews屬性茅撞,這個(gè)屬性是一個(gè)數(shù)組,里面是這個(gè)view的所有子view巨朦,而我們加的控件就位于這個(gè)數(shù)組中米丘,所以實(shí)際上我們的控件對(duì)象是屬于view的,也就是說(shuō)view對(duì)加到它上面的控件是強(qiáng)引用糊啡。我們往ViewController里面拖屬性時(shí)如果使用weak
或者strong
修飾拄查,引用關(guān)系分別如下圖(實(shí)線(xiàn)箭頭代表強(qiáng)引用,虛線(xiàn)箭頭代表弱引用):
從圖上可以看出來(lái)棚蓄,這兩種修飾方式其實(shí)差別不大堕扶,唯一的區(qū)別在于如果中間的view變成了nil,label是否會(huì)被釋放
如果是weak
修飾梭依,view變成了nil時(shí)稍算,label只有VC的弱引用,沒(méi)有被強(qiáng)引用了睛挚,就會(huì)被系統(tǒng)釋放
如果是strong
修飾邪蛔,view變成了nil時(shí),label還被VC強(qiáng)引用扎狱,所以不會(huì)被釋放
那問(wèn)題來(lái)了侧到,什么情況下一個(gè)ViewController的view會(huì)被變成nil呢勃教?
這要追溯到早期的iOS版本了,在iOS6之前匠抗,ViewController生命周期里有個(gè)viewDidUnload方法故源,開(kāi)發(fā)時(shí)間比較久的開(kāi)發(fā)者或者是在一些過(guò)時(shí)的面試題中會(huì)看到這個(gè)方法,它的作用是:
In iOS 5 and earlier, when a low-memory condition occurred and the current view controller's views were not needed, the system could opt to call this method after the view controller's view had been released. This method was your chance to perform any final cleanup. If your view controller stored separate references to the view or its subviews, you could use this method to release those references. You could also use this method to remove references to any objects that you created to support the view but that are no longer needed now that the view is gone. You would not use this method to release user data or any other information that cannot be easily recreated.
In iOS 6 and later, clearing references to views and other objects in your view controller is unnecessary.
At the time this method is called, the view property is nil.
簡(jiǎn)單說(shuō)就是iOS6之前汞贸,當(dāng)系統(tǒng)內(nèi)存過(guò)低時(shí)绳军,系統(tǒng)會(huì)自動(dòng)釋放掉已經(jīng)不需要用到的VC的view,當(dāng)這個(gè)VC的view被Release
之后矢腻,就會(huì)調(diào)用viewDidUnload
方法门驾,如果開(kāi)發(fā)者在VC里面存儲(chǔ)了對(duì)view或其subviews的單獨(dú)引用,則需要在這個(gè)方法里面釋放掉這些引用多柑。
也就是說(shuō)奶是,蘋(píng)果認(rèn)為如果VC的view被release
了,那么它引用的subviews同樣應(yīng)該被release竣灌。結(jié)合上面的圖來(lái)看聂沙,如果用weak
修飾label,那么label的生命周期就和view一樣初嘹,view為nil及汉,label也會(huì)自動(dòng)為nil,而用strong
修飾IBOutlet
屯烦,label不會(huì)自動(dòng)被釋放坷随,開(kāi)發(fā)者需要自己在viewDidUnload
方法里面手動(dòng)把label置為nil。相比之下漫贞,還是前一種方法簡(jiǎn)潔省事甸箱,所以我認(rèn)為蘋(píng)果官方當(dāng)時(shí)建議使用weak
來(lái)修飾應(yīng)該是出于這個(gè)原因育叁。
這也是有時(shí)代背景的迅脐,早期iPhone內(nèi)存都比較小,為了把內(nèi)存利用到極致豪嗽,要盡力去優(yōu)化每個(gè)app的內(nèi)存占用谴蔑,所以在VC的生命周期里面設(shè)計(jì)了這么一個(gè)viewDidUnload
方法,而到了2011龟梦、2012年隐锭,內(nèi)存翻倍了以后,可能蘋(píng)果通過(guò)評(píng)估和監(jiān)測(cè)计贰,發(fā)現(xiàn)viewDidUnload已經(jīng)極少被觸發(fā)钦睡,即使low-memory被觸發(fā),系統(tǒng)也可以通過(guò)其他方式騰挪出更多內(nèi)存給前臺(tái)app(我推測(cè)的)躁倒。所以在iOS6的時(shí)候蘋(píng)果認(rèn)為這個(gè)方法已經(jīng)沒(méi)用了荞怒,就廢棄了這個(gè)方法洒琢。
而iOS6之后,VC的view再也不會(huì)被系統(tǒng)自動(dòng)release
了褐桌,我們作為開(kāi)發(fā)者衰抑,什么情況下會(huì)去主動(dòng)把VC的view置為nil呢?個(gè)人覺(jué)得基本不會(huì)有這樣的操作荧嵌,在我?guī)啄甑拈_(kāi)發(fā)工作中還沒(méi)有遇到呛踊,也想不到有什么場(chǎng)景下是需要保留VC而單獨(dú)把view置nil的。所以在此時(shí)用weak
修飾IBOutlet
的內(nèi)在要求已經(jīng)消失了啦撮,在view不可能為nil的情況下谭网,用weak
還是strong
已經(jīng)沒(méi)有什么區(qū)別,無(wú)論是用weak
還是strong
修飾赃春,IBOutlet
屬性的生命周期都是一樣的蜻底。實(shí)際上我在之前的開(kāi)發(fā)中一直是使用weak來(lái)修飾的,并沒(méi)有因?yàn)檫`背蘋(píng)果的推薦做法而出問(wèn)題聘鳞。
那按照蘋(píng)果建議使用strong
修飾有什么好處呢薄辅?
……
好像沒(méi)有。因?yàn)槟壳翱磥?lái)使用weak
還是strong
沒(méi)有區(qū)別抠璃。
不過(guò)既然必須使用weak
的理由沒(méi)有了站楚,蘋(píng)果官方又建議使用strong
(雖然沒(méi)有在文檔里明說(shuō),但是從新下載的Xcode默認(rèn)是strong
搏嗡,以及IB團(tuán)隊(duì)的工程師的說(shuō)法佐證)窿春,那么我們還是盡量使用strong,跟著官方的建議走采盒。如果未來(lái)蘋(píng)果要在這個(gè)地方做文章旧乞,比如對(duì)IBOutlet
做一些優(yōu)化,那肯定也是按照strong
為前提來(lái)做的磅氨,我們的代碼不會(huì)出問(wèn)題尺栖。