總結(jié)一下工作中遇到的問題
block 是我們工作中常用的一個東西,使用它有很多注意點,我今天聊下項目上線之后使用block引發(fā)一個崩潰問題,
1.先來了解一下block捕獲auto變量
我們知道block會將局部auto變量捕獲到內(nèi)部, 內(nèi)部會強引用變量,這樣會造成循環(huán)引用的問題,這是一個老生常談的問題了,用__weak和__unsafe_unretained 都行但是后者是不安全的,只是在對象釋放的時候__weak會將引用的對象置為nil,而__unsafe_unretained不會,這將會導(dǎo)致野指針的產(chǎn)生,,也就不多說了
解決辦法是使用__weak弱應(yīng)用他,就可以解決循環(huán)引用的問題,那么我們再來看一個現(xiàn)象,我開啟一個子線程執(zhí)行任務(wù),我進入一個界面后,在迅速返回,并且在子線程上模擬一個耗時操作,在的耗時操作的時間內(nèi)內(nèi)我們返回上一層讓我們的控制器銷毀,我們看下會發(fā)生什么事情,結(jié)果如下
由于弱應(yīng)用了self,在控制器被銷毀之后,對象被釋放了,所以無法保留,如果我們?nèi)匀幌雽ο筮M行操作,那就要對對象進行保活,如何進行蹦朊耍活我相信作為一個iOS開發(fā)者都知道的,在block內(nèi)部強引用這個變量,所以是在代碼塊(scope)里面都不會釋放,保證對象的生命周期,結(jié)果如下
本文的重點
看似很完美但是這樣寫代碼就真的沒有問題了嗎?
答案是否定的,這樣寫只能說在block塊里面任務(wù)執(zhí)行完畢時沒有任何問題的,如果你block里面操作了這個資源,剛好這個資源會在其他地方也有使用就會造成資源搶奪在可能,嚴重的會造成app崩潰影響,為了說明這個問題我來演示一下這個現(xiàn)象
控制器A Push控制器B,不做多余的操作
代碼不變的情況下我一步操作,push完控制器我立刻pop我們的上一層控制器,也就是pop到我們的控制器A,當(dāng)然我控制器A的在生命周期的方法里面移除了最后一個元素
我們看下結(jié)果
結(jié)果肯定崩潰,說明線程不安全,2個線程可以同時訪問一個資源,解決資源搶奪的方案就是加鎖,數(shù)據(jù)庫使用databaseQueue保證線程安全訪問,當(dāng)然了還有種處理方案就是不要使用強引用,弱應(yīng)用它就行了,一個nil發(fā)出的消息是不會被響應(yīng)的,如果一定要用strongSelf來達到你項目中某些需求的話,注意block持有對象的生命周期即可,做對應(yīng)的處理,
再次我要感謝bugly,如果沒有buggly,我無法發(fā)現(xiàn)這個問題產(chǎn)生的原因,雖然能意思到可能是線程不安全的問題,但是卻不知道是如何產(chǎn)生的,bugly除了可以實時的監(jiān)控崩潰,還有界面跟蹤功能,這個功能讓我精準的定位到了意識到block延長生命周期所產(chǎn)生的問題,值得一提這個異常上報的埋點功能做的真的不錯
當(dāng)然我們在項目中寫代碼肯定不會怎么寫,一定會判斷一下是否為數(shù)組越界,項目還可以hook一些崩潰的方法,比如數(shù)組 objectAtIndex 方法減少你的項目在線上的崩潰率,對返回的消息做對應(yīng)的處理,在這里我就不啰嗦了,網(wǎng)上有很多這樣的處理方式
后記:記錄這個問題希望對一些人能有所幫助