IOS 實(shí)現(xiàn)一個(gè)死鎖導(dǎo)致 UI 假死的例子
IOS 實(shí)現(xiàn)一個(gè)死鎖導(dǎo)致 UI 假死的例子
現(xiàn)象
當(dāng) APP 啟動(dòng)一段時(shí)間后(約半小時(shí)左右),經(jīng)常會(huì)發(fā)現(xiàn) App 界面出現(xiàn)“凍死”的現(xiàn)象。同時(shí)后臺(tái)輸出:
[CocoaGoPush]WorkThreadProc end
這時(shí) App 呈現(xiàn)“假死”狀態(tài),點(diǎn)擊屏幕任何地方?jīng)]有反應(yīng),iPhone 除了開(kāi)屏關(guān)屏無(wú)任何響應(yīng)(包括按 Home 鍵),當(dāng)然也無(wú)法解鎖(但可以重啟)。如果用 Xcode 終止應(yīng)用程序,則 iPhone 又恢復(fù)正常。
注:App 使用了 CocoaGoPush 框架。
發(fā)現(xiàn)
原來(lái)以為是程序主線程中產(chǎn)生了死循環(huán),導(dǎo)致 UI 無(wú)反應(yīng)。但當(dāng)我點(diǎn)擊 Debug 工具欄中的 Pause 按鈕,列出當(dāng)前運(yùn)行的線程時(shí),則發(fā)現(xiàn)問(wèn)題并不是這樣,而是用于死鎖。調(diào)試暫停后,斷點(diǎn)停在了這一句:
app.gopushLock.lock()// MARK: yhy removed 這行導(dǎo)致主線程死鎖
app.gopushLock 是一個(gè) NSRecursiveLock 對(duì)象:
let gopushLock = NSRecursiveLock()
NSRecursiveLock 是遞歸鎖,該類鎖可以在同一線程多次請(qǐng)求一個(gè)鎖時(shí),不會(huì)引起死鎖。但如果程序員錯(cuò)誤地在兩個(gè)線程中使用了遞歸鎖,則很容易導(dǎo)致“死鎖”出現(xiàn):兩個(gè)線程同時(shí)對(duì)同一個(gè)鎖進(jìn)行加鎖,同時(shí)發(fā)現(xiàn)該鎖已經(jīng)鎖定,彼此等待對(duì)方解鎖,導(dǎo)致兩個(gè)線程都無(wú)法執(zhí)行下去。尤其是有一方是主線程的情況下,主線程被阻塞,UI 呈現(xiàn)假死狀態(tài)。在這個(gè)例子中還發(fā)現(xiàn),gopush 所在的線程也停止了,不再繼續(xù)監(jiān)聽(tīng) gopush 消息和維持心跳。
檢查代碼發(fā)現(xiàn),代碼在另一個(gè)地方使用了這個(gè)遞歸鎖:
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler:{ (response, data, error) -> Void in if (error != nil) { app.gopushLock.lock() app.isGoPushFetchingMessage = false app.gopushLock.unlock() println("-----------GoPush Message Guard fail to fetch offline message. err = \(error.localizedDescription)-----------") ... })
NSURLConection.sendAysnchronousRequest 方法導(dǎo)致請(qǐng)求在新的線程中發(fā)送,因此 app.gopushLock.lock() 實(shí)際上是在子線程中調(diào)用的。而另外一處(第一段代碼)則是在主線程中調(diào)用的,因此導(dǎo)致了“競(jìng)爭(zhēng)”。
解決
方法一
將主線程中的遞歸鎖調(diào)用注釋,只留下子線程中的遞歸鎖調(diào)用。
方法二
在主線程中采用不同的鎖,比如重新定義一個(gè) NSLock 專門用于主線程,和子線程中的 gopushLock 區(qū)別開(kāi)來(lái)。
方法三
將 gopushLock 的類型由 NSRecursiveLock 改為 NSLock。顧名思義,遞歸鎖專門用于循環(huán)或遞歸中需要同步的代碼,但它卻不能避免兩個(gè)線程同時(shí)訪問(wèn)鎖中代碼的情況。而 NSLock 卻恰恰相反,它能避免兩個(gè)線程同時(shí)訪問(wèn)鎖中的代碼,卻不能避免在同一線程中,同步代碼中嵌套加鎖的情況。檢查第二段調(diào)用遞歸鎖的情況,發(fā)現(xiàn)這里根本沒(méi)有必要使用遞歸鎖,因?yàn)榇a中既沒(méi)有遞歸也沒(méi)有循環(huán)。因此可以放心地將 gopushLock 修改為 NSLock 而不是 NSRecursiveLock。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
使用UItableview在iOS應(yīng)用開(kāi)發(fā)中實(shí)現(xiàn)好友列表功能
這篇文章主要介紹了使用UItableview在iOS應(yīng)用開(kāi)發(fā)中實(shí)現(xiàn)一個(gè)好友列表功能的方法,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-12-12IOS設(shè)備上給body綁定click事件不生效的原因及解決辦法
最近在做移動(dòng)端的項(xiàng)目,在ios上對(duì)body綁定click事件實(shí)現(xiàn)事件代理冒泡至某些元素上不生效,怎么回事,如何解決呢?今天小編給大家?guī)?lái)了IOS設(shè)備上給body綁定click事件不生效的原因及解決辦法,一起看看吧2016-09-09IOS 靜態(tài)方法與動(dòng)態(tài)方法詳解
這篇文章主要介紹了IOS 靜態(tài)方法與動(dòng)態(tài)方法詳解的相關(guān)資料,需要的朋友可以參考下2017-02-02iOS應(yīng)用開(kāi)發(fā)中AFNetworking庫(kù)的常用HTTP操作方法小結(jié)
AFNetworking庫(kù)是Objective-C語(yǔ)言寫成的用于處理HTTP的第三方庫(kù),在GitHub上開(kāi)源并且一直在被更新和維護(hù),下面就一起來(lái)看一下iOS應(yīng)用開(kāi)發(fā)中AFNetworking庫(kù)的常用HTTP操作方法小結(jié)2016-05-05詳解iOS 滾動(dòng)視圖的復(fù)用問(wèn)題解決方案
本篇文章主要介紹iOS 滾動(dòng)視圖的復(fù)用問(wèn)題解決方案,具有一定的參考價(jià)值,有興趣的可以了解一下。2016-12-12IOS實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)功能(二)
這篇文章主要介紹了IOS實(shí)現(xiàn)驗(yàn)證碼倒計(jì)時(shí)功能,點(diǎn)擊獲取驗(yàn)證碼,進(jìn)入時(shí)間倒計(jì)時(shí),感興趣的小伙伴們可以參考一下2016-04-04iOS使用runtime修改文本框(TextField)的占位文字顏色
相信大家都知道TextField默認(rèn)的占位顏色也是深灰色,這個(gè)顏色比較難看清,這篇文章給大家介紹如何使用runtime修改TextField文本框的占位文字顏色,有需要的可以參考借鑒.2016-09-09