Swift Crash日誌

Sunny Cheng
2 min readApr 27, 2020

按下UIbarItem 呈現設定畫面— libc++abi.dylib: terminating with uncaught exception of type NSException

發生背景

使用SplitViewController。除了原生Master就會用NavigationController包起來,為了在非PrimaryHidden模式時,瀏覽Detail頁也會有導覽列,參考網路做法,將DetailPage也用NavigationController包起。

PrimaryHidden:導覽列的設定在MasterVC。大尺寸的iPhone直放(wC hR)、小尺寸的iPhone直放橫放(wC hC)

Separate, Overlay:導覽列的設定在DetailVC。大尺寸的iPhone橫放(wC hR)、iPad的直放橫放(wR hR)

需求

在Detail上的導覽列加上一個設定按鈕,按下設定後可以叫出DetailVC。

實作

錯誤

會發生crash並且console沒有打印任何stack出來,只得到一行。

libc++abi.dylib: terminating with uncaught exception of type NSException

問題解析

發現crash在self.present這行,排除storyboard IBOutlet問題。

就UINavigation Controller與SpiltViewController等的呈現VC方法依然在使用者按下按鈕的時候直接crash,因此排除VC堆棧問題。

爬文發現 : https://stackoverflow.com/questions/47228651/nsexception-when-displaying-uialertcontroller-in-own-window

這題情境相似但無解,不過從下面討論串提出Main thread問題,因此轉往執行緒方向下手,觀察crash的時機。

Are you calling this function on the main thread? Perhaps you should surround this code with DispatchQueue.main.async { … } — adamfowlerphoto Nov 10 ’17 at 19:05

Also you are setting rootViewController to a newly created UIViewController(). Shouldn’t you be setting this to the current view controller — adamfowlerphoto Nov 10 ’17 at 19:16

It is on the main thread. I actually removed a dispatch to main to post this because it was on the main thread anyway. The rootViewController is created and added as a root to the new window that overlays the currentViewController. — Siriss Nov 10 ’17 at 19:55

繼續觀察

試著在present setting view的地方切回主線程,發現這樣做crash的機率為百分之百。若不特別寫切回主線程,crash的機率偶發,但容易在tableview滑動或切換UI時點選就crash,將崩潰點聚焦在設定頁面生成上,懷疑是設定頁面的UI設置錯誤。

打開break on exception — http://blog.manbolo.com/2012/01/23/xcode-tips-1-break-on-exceptions

發現崩潰前停在長出新Setting table view cell的地方。試著拔掉這個cell,直接回傳UITableViewCell( ),不會crash,看起來的確在生這個cell的地方出現問題,而且是偶發的。

崩潰的偶發性可以確認跟線程執行順序脫不了關係。

解法

Xib的cell必須要在cellForRowAt前註冊,通常寫在ViewDidLoad中。

發現錯誤在viewDidLoad-setupUI( )這個函式誤用了DispatchQueue.main.async { },導致在cellForRowAt執行時,遇到cell xib尚未註冊完畢就要生成cell導致的crash!

錯誤

因此要思考某些UI設置切換到主線程的正當性,因為下DispatchQueue.main.async { }時,幾乎會先讓block裡面的任務執行往後順延,導致某些UI生成的時候順序錯誤。

切到主線程的使用時機,應該是在call完API切回畫面顯示時使用最為恰當。

另外也發現使用模擬器,因為效能慢,所以“沒有註冊完Xib就生成cell的crash”,相比而言較少碰到(賽到的),但遇到實機模擬時,因iPhone11效能高,造成crash的機率大大增加,以後在測試時也是要特別注意。

修正後

--

--

Sunny Cheng

礦冶工程碩士,職涯第一個轉彎為新加坡市場的業務經理,自學後又轉彎成 OTT 產業的 iOS 工程師。