簡(jiǎn)單的答案:首先在官方文檔《Programming with Objective-C》里面寫到,初學(xué)閱讀的時(shí)候沒有注意到這個(gè)細(xì)節(jié):You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior .
在這里官方叫我們使用copy修飾符,雖然在ARC時(shí)代已經(jīng)不需要再顯式聲明了,也就是使用strong是沒有問題的,但是仍然建議我們使用copy以顯示相關(guān)拷貝行為。問題到這里就基本結(jié)束了。目前使用strong和copy都是沒有問題的。
深入探索: 但是在這里仍然無法解答我的疑惑,需要使用copy修飾符的根本原因是什么。所以繼續(xù)探索。
最終得到的答案是這與block對(duì)象在創(chuàng)建時(shí)是stack對(duì)象有關(guān)。
所以,其實(shí)Objective-C是有它的Stack object的。是的,那就是block.
首先我們先對(duì)block進(jìn)行進(jìn)一步的認(rèn)識(shí)。
在Objective-C語言中,一共有3種類型的block:
_NSConcreteGlobalBlock 全局的靜態(tài)block,不會(huì)訪問任何外部變量。
_NSConcreteStackBlock 保存在棧中的block,當(dāng)函數(shù)返回時(shí)會(huì)被銷毀。
_NSConcreteMallocBlock 保存在堆中的block,當(dāng)引用計(jì)數(shù)為0時(shí)會(huì)被銷毀。
這里我們主要基于內(nèi)存管理的角度對(duì)它們進(jìn)行分類。
NSConcreteGlobalBlock,這種不捕捉外界變量的block是不需要內(nèi)存管理的,這種block不存在于Heap或是Stack而是作為代碼片段存在,類似于C函數(shù)。
NSConcreteStackBlock。這就是這次探索的重點(diǎn)了,需要涉及到外界變量的block在創(chuàng)建的時(shí)候是在stack上面分配空間的,也就是一旦所在函數(shù)返回,則會(huì)被摧毀。這就導(dǎo)致內(nèi)存管理的問題,如果我們希望保存這個(gè)block或者是返回它,如果沒有做進(jìn)一步的copy處理,則必然會(huì)出現(xiàn)問題。
NSConcreteMallocBlock,因此為了解決block作為Stack object的這個(gè)問題,我們最終需要把它拷貝到堆上面來。而此時(shí)NSConcreteMallocBlock扮演的就是這個(gè)角色。
真正的答案: 因此答案便是因?yàn)閎lock在創(chuàng)建時(shí)是stack對(duì)象,如果我們需要在離開當(dāng)前函數(shù)仍能夠使用我們創(chuàng)建的block。我們就需要把它拷貝到堆上以便進(jìn)行以引用計(jì)數(shù)為基礎(chǔ)的內(nèi)存管理。
ARC的疑團(tuán):
解答完最初的問題后,新的問題又出現(xiàn)在我的腦海。那就是ARC是如何進(jìn)行block的內(nèi)存管理的呢,對(duì)于普通的OC對(duì)象之前已經(jīng)總結(jié)過
那么block在ARC下是如何從棧管理正確過渡到堆的管理的呢:
我在網(wǎng)上查閱了許多資料與博文,有部分總結(jié)是:
在ARC下NSConcreteStackBlock類型的block會(huì)替換成NSConcreteMallocBlock類型
其實(shí)這是不夠準(zhǔn)確的,來自蘋果LLVM ARC的文檔中談到:
With the exception of retains done as part of initializing a __strong parameter variable or reading a __weak variable, whenever these semantics call for retaining a value of block-pointer type, it has the effect of a Block_copy. The optimizer may remove such copies when it sees that the result is used only as an argument to a call.
也就是說ARC幫助我們完成了copy的工作,在ARC下,即使你聲明的修飾符是strong,實(shí)際上效果是與聲明為copy一樣的。
因此在ARC情況下,創(chuàng)建的block仍然是NSConcreteStackBlock類型,只不過當(dāng)block被引用或返回時(shí),ARC幫助我們完成了copy和內(nèi)存管理的工作。
總結(jié)和心得:
其實(shí)用一句話總結(jié)便是:
在ARC下,我們可以將block看做一個(gè)正常的OC對(duì)象,與其他對(duì)象的內(nèi)存管理沒什么不同。
有時(shí)我們可能簡(jiǎn)單地從博客和文檔上面得到一句簡(jiǎn)單的結(jié)論就夠了。但是如果我們不斷探索,不斷思考,那么我們的收獲會(huì)更大,更深??赡懿粌H僅是一句知識(shí)點(diǎn),更多的是探索的方法和過程。對(duì)一件事情剝繭抽絲,還原本質(zhì)的過程對(duì)我來說也是一種享受,一種修行。
經(jīng)過了一系列探索,最終理解了block的概念,了解了block的實(shí)現(xiàn),弄懂了block的內(nèi)存管理。
加油,繼續(xù)修行~