? ? ?對于block的循環(huán)引用導(dǎo)致的內(nèi)存泄露,網(wǎng)上的講解文章很多,大部分說的都非常的好,但是描述的更為通俗直白的不多,本文意在通俗直白的解釋block循環(huán)引用導(dǎo)致的內(nèi)存泄露,以及為什么weakSelf與strongSelf這種官方提供的寫法的必要性.(雖然這種重寫為c++語法的過程被拋棄了,但是也可以作為學(xué)習(xí)的途徑)
結(jié)合下面一段代碼來看:
為什么會循環(huán)引用了呢?
其一:當(dāng)前對象強(qiáng)引用著blockTest,這句話很好理解,說白了也就是說當(dāng)前的對象self在內(nèi)存中不釋放,那么blockTest作為self的屬性也不會釋放.
其二:根據(jù)block捕捉上下文變量的原理,block內(nèi)部使用了self,那么將對self強(qiáng)引用.
大部分的解釋到此為止了,明白的人看到這里也沒什么疑惑可是作為尋求答案的不明白的同學(xué)來說,看上去還是似懂非懂,不透徹.那么接下來將多舉例子,從OC重寫到C++代碼層面分析一下這個問題.
首先,內(nèi)存不會被釋放也就是retainCount值不為0,這個道理始終是最基本的,單一變量控制,我們始終只在main函數(shù)中執(zhí)行這樣一句代碼,所以在main函數(shù)大括號內(nèi)代碼走完之前,self的retainCount值是1,blockTest的retainCount也是1,可是由于blockTest強(qiáng)引用了self導(dǎo)致self的retainCount增加了1變?yōu)?,當(dāng)main函數(shù)大括號走完后,self的retainCount減小到1,blockTest的retainCount還是1,self的retainCount沒有回到0,所以self此時和blockTest組成的引用關(guān)系,如同兩條蛇,咬住了對方的尾巴,誰也不松口,死鎖了!
當(dāng)我自己理解到這個節(jié)點的時候,我還是疑惑,block怎么就強(qiáng)引用了呢?為啥用 __weak __typeof(self) weakSelf = self;在block內(nèi)部使用weakSelf就不會強(qiáng)引用呢?為啥要 __strong __typeof(self) strongSelf = weakSelf;才會安全呢?這一系列的機(jī)理到底是怎么回事呢?
比較著咱們來思考一下,先把上面的代碼做一個改動
運行后斷點肯定會進(jìn),也就是內(nèi)存確實釋放了,weakSelf 是個局部變量,這個局部變量會被block捕獲,我們看一下這個使用weakSelf后的中間代碼
1==>表示的是blockTest的結(jié)構(gòu)體形式的表示,這部分中間代碼的具體含義不解釋了,網(wǎng)上有很多講解,
2==>表示的是blockTest的函數(shù)體,無論如何最終執(zhí)行的代碼功能片段都會是個函數(shù),block也不例外
3==>表示的就是- (void)test這個函數(shù).
核心的地方在于1和2,代碼執(zhí)行起來無非就是發(fā)消息,函數(shù)調(diào)用,傳遞參數(shù),返回值,這一系列的動作,
方法執(zhí)行起來以后,3開始執(zhí)行,調(diào)用到了2,2呢有這樣一句話__cself->weakSelf; 看2的形參__cself,其實是結(jié)構(gòu)體__WYTestObject__test_block_impl_0,而結(jié)構(gòu)體就是blockTest,用這種寫法__cself->weakSelf; 具體的編譯以及執(zhí)行肯定是更為細(xì)致精密,但是我們不難看出結(jié)構(gòu)體把weakSelf作為了自己的屬性,或者說直接捕獲的是weakSelf這個局部變量作為自己的屬性,但是可以通俗直白的理解為結(jié)構(gòu)體直接引用了weakSelf內(nèi)存空間想使用weakself中保存的self指針,而這個weakSelf并沒有增加self的引用計數(shù),所以self依然可以釋放.如果說這樣就砍斷了循環(huán)引用,那么蘋果為什么要提倡weak,strong寫法呢?請看下面的代碼
類似以上的情況我們不會總遇到,因此很多人都是只寫一個weakSelf在使用著,但是這樣崩潰的情況在更復(fù)雜的情況下,如果隱藏為nil的邏輯很深的話,出現(xiàn)不必現(xiàn)的bug就不好調(diào)試了.那么咱們在回到簡單的oc代碼中分析一下strongSelf寫法為什么既能避免這種為空情況,又能不產(chǎn)生循環(huán)引用的呢?請看下面的代碼.
只是多了箭頭所指的一句代碼,咱們看看中間代碼是此時又是什么樣的.
相比之前沒加入strongSelf的時候函數(shù)__WYTestObject__test_block_func_0 多了一句話WYTestObject * strongSelf = ?weakSelf,strongSelf賦值得到的是self(由weakSelf傳過來的),這使得在函數(shù)體運行期間self是不會被釋放的,__WYTestObject__test_block_func_0函數(shù)實際上就是blockTest的功能模塊,實際上執(zhí)行blockTest也就是在執(zhí)行__WYTestObject__test_block_func_0這個函數(shù),既然是函數(shù),那么大括號走完其函數(shù)體重的局部變量就會被釋放,也就是說函數(shù)執(zhí)行完以后,strongSelf作為局部變量正常釋放了,strongSelf也不屬于blockTest結(jié)構(gòu)體的屬性,weakSelf與strongSelf一內(nèi)一外,意義是不一樣的,所以下面的代碼:
以上代碼viewWy既能正常釋放,又不會崩潰,這也就是weak和strong配合使用的意義所在.