1. 程式人生 > >多執行緒導致的iOS閃退分析

多執行緒導致的iOS閃退分析

前段時間做了一個APP,在測試的時候遇到了很奇怪的閃退情況。

這個APP是有關聲音處理的:裝置一邊錄音,一邊對聲音進行處理。所以需要2個執行緒,一個執行緒將錄音儲存下來,另一個處理儲存下來的聲音。測試的時候,會在1~10min之內,不定時、無預兆的出現閃退的情況,報的錯也各不一樣,有的是

1)”NSGenericException ‘Collection was mutated while being enumerated”

或者:

2)”pointer being freed was not allocated”

還有的閃退資訊提示記憶體用的太多:

3)”crash due to memory pressure”

更有的打出了天書:

4)”First throw call stack:

(0x30170ecb 0x3a907ce7 0x301709b9 0x4a177 0x40a97 0x30b5bab5 0x3013bf0f 0x3013bb2b 0x30139eb3 0x300a4729 0x300a450b 0x350036d3 0x32a05871 0x4f4b9 0x3ae05ab7)”

而時間要求又比較急,真是有點焦頭爛額。經過1天多的研究,這些問題被一一搞定。

首先,第3條,明顯是生成了記憶體而沒有釋放。咦,Objective-C不是有ARC嗎,自動處理記憶體啊,好長時間都沒有因為記憶體煩惱了。但是聲音的處理都是比較底層的,用的都是C語言,對這部分的記憶體,ARC就愛莫能助了。處理起來也不麻煩了,先用Instruments定位一下,哪邊洩漏的記憶體,然後對所有malloc出來的記憶體塊,用完了都free掉。再試一下,記憶體的增加果然慢下來了。

第1條,”Collection was mutated while being enumerated”,意思是,一個物件(一般是NSArray什麼的)在被訪問的時候,這個物件發生了變化,導致程式掛掉了。在我的程式裡,這個物件就是儲存聲音資料的東西,暫且叫它data。第1個執行緒會源源不斷的向data裡寫入新資料,並將舊資料刪掉。而第2個執行緒,則會定時讀取data的內容並做處理。因為這兩個執行緒每次操作data的時間都比較短,所以它們同時操作的情況不是很常見,所以一般程式也能堅持個幾分鐘。然而它們一旦同時對data進行操作/訪問,程式就掛了。

解決方法也很簡單,Objective-C裡有一個語法,專門處理這樣的事:@synchronized(引數){程式碼塊}

當兩個@synchronized程式碼塊的引數相同的時候,這兩個程式碼塊是不能同時操作的。對於引數,我們經常使用self來做引數。例如:

執行緒1:

@synchronized(self){

//寫data

}

執行緒2:

@synchronized(self){

//讀data

}

當執行緒1在寫data時,執行緒2只能等著。這樣就避免了同時多執行緒同時讀/寫global資料塊會出現的閃退問題。

第2條,”pointer being freed was not allocated”,也是因為多執行緒同時寫data的問題,但有一些特殊。data裡只包含了最近一段時間的聲音資料,當data儲存的太多了,就會先將舊資料刪了,再將新資料存起來。問題是執行緒1的呼叫次數非常快,達到1秒鐘50次。有時候上一次呼叫a還沒結束,下一次呼叫b又過來了,這時候就可能會出問題:a檢測到data裡的資料太多了,就將最舊的資料刪了。然而沒等a真正將資料刪除,b又來了,它也要將最舊的資料刪了,這樣同一個資料就要被free兩次,編譯器就要叫:尼碼這個指標裡已經空了,你還要老子再free它,老子不幹了!

解決方法同上,也加一個@synchronized,將這段資料塊包起來,告訴編譯器:為了保證服務質量,每次只向一個執行緒提供服務,等上一位大爺舒坦了,再讓下一個進來。

最後,有時候編譯器還會抽風,只告訴你,”我要掛了!”(EXC_BAD_ACCESS),然後嘔吐出一大堆排洩物:

“First throw call stack:

(0x30170ecb 0x3a907ce7 0x301709b9 0x4a177 0x40a97 0x30b5bab5 0x3013bf0f 0x3013bb2b 0x30139eb3 0x300a4729 0x300a450b 0x350036d3 0x32a05871 0x4f4b9 0x3ae05ab7)”

作為程式設計師,我們要從這堆排洩物中找到編譯器的病因,看看它到底吃了啥:

在AppDelegate.m里加入這個函式:

void uncaughtExceptionHandler(NSException *exception) {

NSLog(@”CRASH: %@”, exception);

NSLog(@”Stack Trace: %@”, [exception callStackSymbols]);

//Internal error reporting

}

然後:

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);

}

這樣,當編譯器掛了的時候,就會打印出這樣的東西:

0   CoreFoundation                      0x30c16f9b <redacted> + 154,
1   libobjc.A.dylib                     0x3b491ccf objc_exception_throw + 38,
2   CoreFoundation                      0x30b4da39 <redacted> + 176,
3   TEST                    0x001ce2c9 -[TestBaseViewController viewDidLoad] + 848,

我們就能看到,問題出在[TestBaseViewController viewDidLoad]函式裡(請忽略後面的+848,我不知道是什麼意思,貌似也沒人知道)。雖然無法定位到具體哪一行,但至少是大大縮小的範圍。

另外,還有一個關於崩潰定位的小技巧:在所有你懷疑會出現閃退的地方附近,瘋狂的NSLog,這樣,當閃退的時候,很快就能定位到在哪個位置閃退了。

對於程式設計師來說,定位到問題意味著問題解決了90%。

相關推薦

執行導致iOS退分析

前段時間做了一個APP,在測試的時候遇到了很奇怪的閃退情況。 這個APP是有關聲音處理的:裝置一邊錄音,一邊對聲音進行處理。所以需要2個執行緒,一個執行緒將錄音儲存下來,另一個處理儲存下來的聲音。測試的時候,會在1~10min之內,不定時、無預兆的出現閃退的情況,報的錯

《Exploring in UE4》執行機制詳解[原理分析]

目錄 一.概述 二."標準"多執行緒 三.AsyncTask系統 3.1 FQueuedThreadPool執行緒池 3.2 Asyntask與IQueuedWork 3.3 其他相關技術細節 四.TaskGraph系統 4.1 從Tick函式談起 4.2 T

python執行探討 基於原始碼 初步分析

這篇文章 應該有人需要 因為我現在都需要 所以標了個關鍵字初步 摘自百度百科 儘管提高CPU的時鐘頻率和增加快取容量後的確可以改善效能,但這樣的CPU效能提高在技術上存在較大的難度。實際上在應用中基於很多原因,CPU的執行單元都沒有被充分使用。如果CPU不能正常讀取資料(

python執行的幾種情形分析-三種情況

情形一:預設情況   預設情況,只開啟執行緒,那麼,主執行緒結束,其他子執行緒可能還沒結束。   只使用t=threading.Thead(target=fun),t.start()。 import threading import time def run(): tim

執行計算pi效率對比分析

繼續昨天的計算pi的程式 分配不同的執行緒數量得到的不同的執行時間對比 可以看到很明顯的 從一個執行緒變成兩個執行緒確實時間減少了一般左右,但是繼續增加執行緒數量卻不怎麼影響結果。這是因為我電腦的CPU型號為Intel 酷睿i3 370M,雙核四執行緒 ,所以雙執行緒

Chromium執行模型設計和實現分析

       Chromium除了遠近聞名的多程序架構之外,它的多執行緒模型也相當引人注目的。Chromium的多程序架構是為了解決網頁的穩定性問題,而多執行緒模型則是為了解決網頁的卡頓問題。為了達到這個目的,Chromium的多執行緒模型是基於非同步通訊的。也就是說,一個執

ActiveX控制元件中使用執行導致的問題

用VC++6.0寫MFC ActiveX控制元件,就是實現個數據統計功能,呼叫SqlServer的儲存過程,計算量比較大,呼叫時間比較長,所以要把進度顯示出來(方法前文有述),用到多執行緒。需要線上程函式裡把計算的結果通過控制元件事件傳出去。 問題很奇怪,本來以為沒問題,使用

WebView中使用Label標籤導致iOS退

在WebView中載入HTML5程式碼:<div class="ui-form-item ui-form-item-show ui-form-item-link ui-border-b upCollPage" data-type="input" onclick=""&g

執行導致的request請求引數獲取不到問題

症狀: 程式碼還是按照常規寫法寫的,莫名其妙的忽然使用request拿不到引數。 如上面的程式碼,springmvc幫我們

關於ios使用執行導致程式不規律的退問題解決

之前自己寫的一個小專案,使用支執行緒的目的就是為了一些運算 或者伺服器間的互動。  [NSThreaddetachNewThreadSelector:@selector(setLoadData)toTarget:selfwithObject:nil]; 開啟一個執行緒

iOS的三種執行技術 對比分析

1 #pragma mark 模仿下載網路影象 2 - (IBAction)operationDemo3:(id)sender 3 { 4 // 1. 下載 5 NSBlockOperation *op1 = [NSBlockOperation blockOperationWit

iOS執行中,佇列和執行的排列組合結果分析

本文是對以往學習的多執行緒中知識點的一個整理。 多執行緒中的佇列有:序列佇列,併發佇列,全域性佇列,主佇列。 執行的方法有:同步執行和非同步執行。那麼兩兩一組合會有哪些注意事項呢? 如果不是在董鉑然部落格園看到這邊文章請 點選檢視原文 提到多執行緒,也就是四種,pthread,NSthread,GCD

Objective-C高階程式設計:iOS與OS X執行和記憶體管理

這篇文章主要給大家講解一下GCD的平時不太常用的API,以及文末會貼出GCD定時器的一個小例子。 需要學習的朋友可以通過網盤免費下載pdf版 (先點選普通下載-----再選擇普通使用者就能免費下載了)http://putpan.com/fs/cy1i1beebn7s0h4u9/ 1.G

[讀書筆記]iOS與OS X執行和記憶體管理 [GCD部分]

3.2 GCD的API 蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block

Android執行分析之一:使用Thread非同步下載影象

Android多執行緒分析之一:使用Thread非同步下載影象 羅朝輝 (http://blog.csdn.net/kesalin) CC 許可,轉載請註明出處 打算整理一下對 Android Framework 中多執行緒相關知識的理解,主要集中在 Fra

iOS總結-執行篇之NSOperation和NSOperationQueue

參考:https://www.jianshu.com/p/4b1d77054b35 NSOperation/NSOperationQueue是基於GCD更高一層的封裝,完全面向物件。 優點:1.可新增完成的程式碼塊,在操作完成後執行        

iOS總結-執行篇之GCD之三

dispatch_barrier_async :GCD柵欄方法 dispatch_apply dispatch_semaphore:GCD訊號量 持有計數的訊號,計數為0時等待,不可通過.計數為1或者大於1時,計數減1且不等待,可通過. dispatch_semap

iOS總結-執行篇之GCD之二

dispatch_group dispatch_wait dispatch_group_enter/dispatch_group_leave dispatch_group_enter標誌一個任務加入group,未執行完畢任務數+1  dispatch_group

iOS總結-執行篇之GCD之一

程序:作業系統資源分配的基本單位   執行緒:任務排程和執行的基本單位 一個程序裡面可以有多個執行緒. GCD自動利用CPU核心(如雙核,四核),會自動管理執行緒的生命週期(建立執行緒,排程任務,銷燬執行緒) 而NSOperation Queue是可以管理執行緒的. 佇列

iOS - 知識梳理(執行

多執行緒:一個程序裡面開啟多條執行緒,每條執行緒可以單獨的執行不同的任務。 iOS實現多執行緒的方式: 1、pthread(C寫的、基本不用) 2、NSThread 3、gcd 4、NSOperation 下面分別介紹下後三個常用的多執行緒方式 NSThread: 使用方式