iOS如何避免應(yīng)用崩潰

最近在掘金翻譯計(jì)劃和小伙伴一起翻譯的一篇的iOS相關(guān)文章撬即,文中能夠了解到一些iOS的文件讀取機(jī)制和我們線上閃退問題如何發(fā)現(xiàn),以及一些處理方案呈队。其實(shí)還是有很多iOS開發(fā)的小技巧的剥槐。

原文地址:How Not to Crash

原文作者:Padraig

譯文出自:掘金翻譯計(jì)劃

譯者:Gocy

校對(duì)者:lovelyCiTY,DeadLion

如何避免應(yīng)用崩潰

應(yīng)用崩潰時(shí)有發(fā)生。崩潰會(huì)打斷用戶當(dāng)前的工作流掂咒,導(dǎo)致數(shù)據(jù)的丟失才沧,還會(huì)擾亂應(yīng)用在后臺(tái)的操作。對(duì)于開發(fā)者而言绍刮,那些最難修復(fù)的崩潰往往是那些難以重現(xiàn)温圆,甚至難以檢測(cè)到的崩潰。

我最近發(fā)現(xiàn)并修復(fù)了一個(gè) bug 孩革,而它正是導(dǎo)致 Castro 反復(fù)出現(xiàn)難以檢測(cè)的崩潰的罪魁禍?zhǔn)祝ㄗg者注: Castro 是原文作者開發(fā)的一款應(yīng)用)岁歉,我將處理這個(gè)問題的過程分享給大家并附上一些我的建議,或許能幫助你定位類似的問題。

我和 Oisin 在九月份發(fā)布了 Castro 2.1 版本锅移,那之后不久熔掺,從 iTunes Connect 上報(bào)的 Castro 崩潰數(shù)量便急劇上升。

iTunes Connect 崩潰上報(bào)

有趣的是非剃,這些崩潰并沒有出現(xiàn)在我們平時(shí)使用的崩潰上報(bào)服務(wù) HockeyApp 中置逻,因此我們實(shí)際上在晚些時(shí)候才發(fā)現(xiàn)我們的應(yīng)用出現(xiàn)了問題。想要查看到應(yīng)用的所有崩潰备绽,開發(fā)者需要從 iTunes Connect 或是 Xcode 中查看崩潰上報(bào)券坞。(更新: Greg Parker指出“第三方崩潰上報(bào)系統(tǒng)在對(duì)應(yīng)的應(yīng)用進(jìn)程中建立 handler 來記錄應(yīng)用行為,但如果操作系統(tǒng)從外部終止進(jìn)程肺素,這個(gè) handler 就永遠(yuǎn)無法執(zhí)行了恨锚。”)倍靡,另外猴伶, HockeyApp 的聯(lián)合創(chuàng)始人 Andreas Linde引用了一篇文章來界定那些Hockey 能以及不能檢測(cè)到的崩潰。)

如果你是一名應(yīng)用開發(fā)者并且登陸了開發(fā)者賬號(hào)塌西, Xcode 允許你檢視 Apple 官方從你的當(dāng)前帳號(hào)下的 app 用戶那收集到的崩潰日志他挎。這項(xiàng)功能在 Window 導(dǎo)航欄下的 Organizer 窗口中的 Crashes 標(biāo)簽中。你可以選擇特定的應(yīng)用版本捡需, Xcode 會(huì)下載 Apple 從用戶手上收集到的崩潰日志雇盖,前提是用戶同意將信息分享給開發(fā)者。

我發(fā)現(xiàn) Xcode 的這個(gè)功能也非常容易崩潰栖忠,尤其是當(dāng)點(diǎn)擊崩潰日志中線程的詳情按鈕進(jìn)行切換的時(shí)候。一個(gè)簡(jiǎn)便的解決方案是贸街,在列表中右鍵選中相應(yīng)的崩潰庵寞,并選擇在 Finder 中顯示。如果你要研究研究包中的內(nèi)容薛匪,你可以把這些崩潰日志簡(jiǎn)單地當(dāng)作文本文件捐川。

分析崩潰原因

許多不同的代碼路徑都觸發(fā)了這個(gè)崩潰,但崩潰最終都指向一個(gè)數(shù)據(jù)庫查詢方法逸尖。

一開始我認(rèn)為是多線程引發(fā)的問題古沥,畢竟在被線程問題折磨了多年之后,我總是第一時(shí)間想到它娇跟。我以文本文件的格式打開崩潰日志岩齿,因?yàn)檫@樣比直接用 Xcode 打開展示了更多的細(xì)節(jié)。崩潰的異常類型是EXC_CRASH (SIGKILL)苞俘,對(duì)應(yīng)的信息是EXC_CORPSE_NOTIFY盹沈,程序被終止的原因是Code 0xdead10cc。于是我試著找出0xdead10cc是什么含義吃谣。 Google 或是 Apple Developer 論壇都沒有多少相關(guān)的信息乞封,但Technical Note 2151中提到:

異常碼 0xdead10cc 出現(xiàn)意味著應(yīng)用程序因?yàn)樵诤笈_(tái)操作系統(tǒng)資源(譬如通訊錄數(shù)據(jù)庫)而被 iOS 系統(tǒng)終止做裙。

這時(shí)候我意識(shí)到 iOS 強(qiáng)制關(guān)閉我的應(yīng)用是因?yàn)槲疫`反了系統(tǒng)規(guī)則,而不是說我的代碼出了什么小問題肃晚。但是锚贱, Castro 并沒有用到通訊錄數(shù)據(jù)庫或是任何我能想到的類似的系統(tǒng)資源。我還懷疑原因是不是應(yīng)用在后臺(tái)長(zhǎng)時(shí)間運(yùn)行而沒有取消关串,但我也發(fā)現(xiàn)日志中有一些應(yīng)用僅僅運(yùn)行了兩秒鐘就發(fā)生崩潰的記錄拧廊。

經(jīng)過推理,我最終將可能原因定位到我們的數(shù)據(jù)庫相關(guān)的 SQLite 文件上悍缠,因?yàn)榻^大部分的堆棧信息都顯示崩潰是在操作數(shù)據(jù)庫的時(shí)候發(fā)生的卦绣。但 2.1 版本上的哪個(gè)改動(dòng),突然就引起了這個(gè)崩潰呢飞蚓?

應(yīng)用的共享容器

Castro 2.1 版本引入了對(duì) iMessage 的支持來讓用戶輕松地分享他們最近聽過的播客滤港。為了讓 message app 能夠訪問數(shù)據(jù)庫,我們將數(shù)據(jù)庫邏輯移動(dòng)到了應(yīng)用共享容器中趴拧。

我猜想文件的鎖機(jī)制對(duì)在共享區(qū)域的文件有更嚴(yán)格的要求溅漾。或許當(dāng) iOS 準(zhǔn)備掛起一個(gè)應(yīng)用的時(shí)候著榴,系統(tǒng)會(huì)檢查這個(gè)應(yīng)用是否正在使用一些可能被其他進(jìn)程使用的文件添履,如果有, iOS 就會(huì)直接終止這個(gè)應(yīng)用脑又。這看起來是個(gè)有理有據(jù)的解釋暮胧。

如何重現(xiàn)崩潰

如何重現(xiàn)正在修復(fù)的崩潰是鍛煉開發(fā)者的絕佳實(shí)踐。這可能涉及到臨時(shí)改寫一部分代碼來刻意提高崩潰出現(xiàn)的可能性问麸。如果我們能穩(wěn)定地看到崩潰的發(fā)生往衷,就能夠逐步的驗(yàn)證我們的猜測(cè),同時(shí)我們測(cè)試修復(fù)的正確性就有了參考严卖。而與之對(duì)應(yīng)的另一個(gè)方法是盲目地進(jìn)行修復(fù)席舍,發(fā)布版本,然后等著看是否會(huì)有崩潰上報(bào)哮笆。有時(shí)候来颤,只有盲目修復(fù)一條路可走,但這條路枯燥乏味稠肘,而且到頭來應(yīng)用依然不斷地在用戶側(cè)發(fā)生崩潰福铅。

而這個(gè)崩潰就非常不容易重現(xiàn),我覺得這里批評(píng)一下 iOS 的開發(fā)環(huán)境并不過分项阴。操作系統(tǒng)粗野地執(zhí)行著自己的規(guī)則本讥,大部分時(shí)候,這樣做很好,因?yàn)檫@樣可以提高安全性拷沸,延長(zhǎng)電池壽命和穩(wěn)定性色查。但在這樣的大環(huán)境下進(jìn)行應(yīng)用的測(cè)試和修復(fù),就增加了不必要的麻煩撞芍。這些規(guī)則的變化悄無聲息秧了,而要人為地在應(yīng)用周期可能出現(xiàn)的每一個(gè)狀態(tài)下進(jìn)行測(cè)試非常不方便,有時(shí)候甚至根本無法完成序无。

在本例中验毡,我意識(shí)到在 debugger 模式下進(jìn)行測(cè)試無法觸發(fā)程序后臺(tái)掛起的狀態(tài)。實(shí)際上帝嗡,debugger會(huì)阻止掛起晶通,而且模擬器也不會(huì)精準(zhǔn)的模擬掛起。如果不在 debugger 模式下的話哟玷,那么就只剩下反復(fù)測(cè)試然后查看設(shè)備日志這一個(gè)選擇了狮辽。

macOS Sierra 上的全新 Console App 提供了訪問任何當(dāng)前連接中的 iPhone 的系統(tǒng)日志的功能,而在 Sierra 之前巢寡,我都是靠 Lemon Jar 的iOS Console來完成這個(gè)操作喉脖,但是,看到 Apple 官方提供能夠訪問日志的工具抑月,了解這樣的技術(shù)是被官方所接受并支持的树叽,感覺也是極好的。你值得花時(shí)間去學(xué)習(xí)如何使用全新的 Console App 谦絮,它呈現(xiàn)出許多 Xcode 調(diào)試器無法呈現(xiàn)的操作题诵。由于這份日志是整個(gè)系統(tǒng)所有日志的統(tǒng)一輸出,所以會(huì)有許多不相關(guān)的冗余信息层皱,但你可以輕松地創(chuàng)建一個(gè)過濾器仇轻,將顯示的內(nèi)容限定在與你的應(yīng)用相關(guān)的范圍內(nèi)。

為了刻意重現(xiàn)崩潰dead10cc:

我在applicationDidEnterBackground方法中做了幾百次數(shù)據(jù)庫查詢操作奶甘。

在我的 Mac 上打開 Console 應(yīng)用,并過濾信息祭椰,僅顯示 Castro 相關(guān)臭家。

我從 Xcode 上運(yùn)行安裝應(yīng)用,但以直接點(diǎn)擊應(yīng)用圖標(biāo)的形式打開應(yīng)用方淤。

我按 Home 鍵將應(yīng)用退到后臺(tái)钉赁,并立刻打開 Pokémon Go ,以期系統(tǒng)會(huì)由于內(nèi)存吃緊而掛起 Castro 携茂。

在重復(fù)了幾次上述步驟之后你踩,我發(fā)現(xiàn) Console 中已經(jīng)出現(xiàn)了我嘗試重現(xiàn)的崩潰信息。調(diào)用堆棧看起來和真實(shí)場(chǎng)景的崩潰一模一樣带膜,現(xiàn)在我就非常自信地知道崩潰的原因何在了吩谦。

接著我發(fā)現(xiàn)并修復(fù)了項(xiàng)目中一個(gè)在后臺(tái)訪問數(shù)據(jù)庫觸發(fā)的錯(cuò)誤:在網(wǎng)絡(luò)狀況變化時(shí),應(yīng)用會(huì)在沒有創(chuàng)建 background task 的情況下進(jìn)行數(shù)據(jù)庫刷新操作膝藕。如果在刷新操作尚未完成時(shí)應(yīng)用進(jìn)入掛起狀態(tài)式廷, iOS 就會(huì)強(qiáng)制終止應(yīng)用運(yùn)行。

理解后臺(tái)獲劝磐臁( Background Fetch Gotcha )

我還要再分享一件讓我驚訝的事情滑废。在 Castro 2 版本,我們?cè)谟行聞〖l(fā)布后通知客戶端袜爪,從而客戶端會(huì)刷新用戶的推送內(nèi)容蠕趁。當(dāng) iOS 將這條消息轉(zhuǎn)發(fā)給我們的應(yīng)用的時(shí)候,它會(huì)調(diào)用didReceiveRemoteNotification方法辛馆,而在這個(gè)方法中俺陋,我們有一個(gè) completion block 的回調(diào)。官方文檔中提到:

你的應(yīng)用至多只有三十秒時(shí)間來處理推送消息怀各,而后調(diào)用相應(yīng)的 completion handler block 倔韭。實(shí)際開發(fā)中,一旦你處理完推送瓢对,就應(yīng)該盡快地調(diào)用這個(gè) handler block 寿酌。系統(tǒng)會(huì)記錄下應(yīng)用在后臺(tái)所耗費(fèi)的時(shí)間、電量硕蛹、以及數(shù)據(jù)處理所消耗的流量醇疼。

令人抓狂的點(diǎn)在于:就像我在前文中提到的, Castro 有時(shí)候運(yùn)行不到兩秒就被終止了法焰,我從調(diào)用棧信息明確看到這時(shí)候還沒有調(diào)用 completion block 秧荆,所以說,盡管文檔寫著說應(yīng)用可以安安心心的運(yùn)行個(gè) 30 秒埃仪,但我的應(yīng)用還是被掛起了乙濒。

這實(shí)在是出乎意料,于是我決定使用一次開發(fā)者 Technical Support Incidents 來看看到底發(fā)生了什么事(譯者注:Technical Support Incidents是蘋果提供的一項(xiàng)技術(shù)支持服務(wù) )卵蛉。我從負(fù)責(zé)我的請(qǐng)求的工程師 Kevin Elliott 那得到了一些非常有幫助的回應(yīng):

正如我所懷疑的那樣颁股,dead10cc問題源于文件上鎖:

“真正觸發(fā)崩潰的原因是, iOS 在掛起你的應(yīng)用的時(shí)候傻丝,檢查到在你的應(yīng)用容器中有一個(gè)被鎖住的文件(本例中就是一個(gè) SQLite 鎖)甘有。這個(gè)檢查的目的在于管理和減少應(yīng)用內(nèi)的數(shù)據(jù)損壞。本例的問題在于葡缰,一個(gè)文件處于被鎖狀態(tài)亏掀,意味著它很可能正在被修改忱反,處于一個(gè)數(shù)據(jù)不連貫的狀態(tài)。也就是說滤愕,一個(gè)應(yīng)用對(duì)一個(gè)文件加鎖的唯一理由就是它接下來要對(duì)這個(gè)文件進(jìn)行一系列的讀/寫操作温算,并且需要保證這些寫操作能夠順利完成而不被其他的寫操作插隊(duì)。簡(jiǎn)單的說就是该互,一個(gè)文件還處于被鎖狀態(tài)意味著對(duì)應(yīng)的應(yīng)用還沒有完成數(shù)據(jù)的寫入米者,而處于這種狀態(tài)下的文件可能會(huì)有以下的幾個(gè)問題:

如果應(yīng)用在掛起狀態(tài)被強(qiáng)制終止,那些“應(yīng)該卻還未被寫入”的數(shù)據(jù)便不會(huì)被寫入宇智,導(dǎo)致數(shù)據(jù)損壞蔓搞。

如果這個(gè)文件在兩個(gè)應(yīng)用之間共享,此時(shí)第二個(gè)應(yīng)用/應(yīng)用擴(kuò)展開始運(yùn)行随橘,那這個(gè)應(yīng)用將要么被迫解除這個(gè)鎖喂分,并試圖將文件恢復(fù)到一個(gè)穩(wěn)定連續(xù)的狀態(tài),而讓第一個(gè)應(yīng)用繼續(xù)處在一個(gè)不連續(xù)的狀態(tài)机蔗,要么就完全忽略這個(gè)共享文件蒲祈。”

至于那 30 秒的后臺(tái)運(yùn)行時(shí)間:

...正確的做法應(yīng)該是徹底規(guī)避這個(gè)問題 - 如果你不能在 delegate 方法中完成所有的操作(譯者注:這里的 delegate 方法即指didReceiveRemoteNotification方法)萝嘁,那么就直接另起一個(gè) background task 梆掸,這樣 iOS 在(completion block 中)掛起你的應(yīng)用之前就會(huì)先通知你...

另外, Kevin 也建議應(yīng)用進(jìn)入后臺(tái)的時(shí)候應(yīng)該關(guān)閉數(shù)據(jù)庫牙言,以此來確保應(yīng)用已經(jīng)完成了數(shù)據(jù)刷新并能更準(zhǔn)確的找到少見的 bug :

將關(guān)閉文件作為一項(xiàng)常規(guī)操作酸钦,從而將一些隱蔽而奇怪的 bug (應(yīng)用在后臺(tái)有時(shí)不太對(duì)勁),轉(zhuǎn)化成穩(wěn)定出現(xiàn)的問題(應(yīng)用在后臺(tái)無法正常運(yùn)行)咱枉,這時(shí)候你就可以直接去定位問題了卑硫。

這看起來是個(gè)明智的做法;我從沒想過要在應(yīng)用進(jìn)入后臺(tái)的時(shí)候關(guān)閉一部分功能蚕断,但其實(shí)這么做非常合理欢伏。在 Castro 的下一個(gè)版本更新中,我將會(huì)嘗試在退后臺(tái)時(shí)關(guān)閉數(shù)據(jù)庫亿乳。

總結(jié)

通過把任何會(huì)在后臺(tái)持續(xù)運(yùn)行的操作放到一系列 background task 中硝拧,我成功地在 beta 版本中解決了這一問題。我們會(huì)盡快發(fā)布包含這個(gè)修復(fù)的更新葛假。

以下是我所學(xué)到的東西的小小總結(jié):

Apple 官方會(huì)上報(bào)一些其他服務(wù)不會(huì)上報(bào)的崩潰障陶。所以除了外部服務(wù)之外,也要查看在 iTunes Connect 和 Xcode 上面的崩潰信息桐款。

文件的鎖機(jī)制對(duì)于在共享區(qū)域的文件有著更嚴(yán)格的要求。

依賴于 background fetch 的 completion block 是遠(yuǎn)遠(yuǎn)不夠的夷恍,不要在一個(gè)現(xiàn)行的 background task 之外做任何后臺(tái)操作魔眨。

想要調(diào)試那些僅僅在應(yīng)用生命周期的特定條件下出現(xiàn)的問題是非常困難的媳维。如果你還沒有嘗試過新的 Sierra Console.app ,現(xiàn)在就開始學(xué)習(xí)吧遏暴。

別忘了Technical Support Incidents侄刽,你每年的開發(fā)者賬號(hào)可都為這兩次機(jī)會(huì)買了單噢。(多謝啦 Kevin 大兄弟E罅埂)

如果你欣賞這篇文章州丹,或許你也會(huì)對(duì)Supertop podcast和我們的播客應(yīng)用Castro感興趣。

這篇文章的標(biāo)題引用了 Brent Simmons 的"How Not to Crash”系列杂彭,我強(qiáng)烈推薦還沒看過的讀者去看看這個(gè)系列墓毒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市亲怠,隨后出現(xiàn)的幾起案子所计,更是在濱河造成了極大的恐慌,老刑警劉巖团秽,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件主胧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡习勤,警方通過查閱死者的電腦和手機(jī)踪栋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來图毕,“玉大人夷都,你說我怎么就攤上這事∥庑” “怎么了损肛?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)荣瑟。 經(jīng)常有香客問我治拿,道長(zhǎng),這世上最難降的妖魔是什么笆焰? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任劫谅,我火速辦了婚禮,結(jié)果婚禮上嚷掠,老公的妹妹穿的比我還像新娘捏检。我一直安慰自己,他們只是感情好不皆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布贯城。 她就那樣靜靜地躺著,像睡著了一般霹娄。 火紅的嫁衣襯著肌膚如雪能犯。 梳的紋絲不亂的頭發(fā)上鲫骗,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音踩晶,去河邊找鬼执泰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛渡蜻,可吹牛的內(nèi)容都是我干的术吝。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼茸苇,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼排苍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起税弃,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤纪岁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后则果,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幔翰,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年西壮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了遗增。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡款青,死狀恐怖做修,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抡草,我是刑警寧澤饰及,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站康震,受9級(jí)特大地震影響燎含,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腿短,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一屏箍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧橘忱,春花似錦赴魁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凝颇,卻和暖如春潘拱,著一層夾襖步出監(jiān)牢的瞬間秉继,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國(guó)打工泽铛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人辑鲤。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓盔腔,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親月褥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子弛随,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容