OC中的Crash異常的總結和捕獲方法

2021-08-02 18:47:50 字數 3528 閱讀 2426

oc層的異常是ios開發中最最最好抓取和分析的異常了。製造乙個典型的oc異常簡直再簡單不過:

1

2

3

4

5

6

7

nsstring *str = nil;

nsdictionary *dic = @;

//or

nsarray *array= @[@"a",@"b",@"c"];

[array objectatindex:5];

//or

nsassert(false, @"oc exception");

另外,oc異常還有乙個非常好用的特性是可以用trycatch抓住(雖然蘋果並不建議這麼使用)。例如:

1

2

3

4

5

@try  @catch (n***ception *exception) 

就可以獲取到當前丟擲異常並且阻止異常繼續往外拋導致程式崩潰。雖然蘋果真的不建議這樣做。對於程式真的往外丟擲並且我們很難catch到的異常,比如介面和第三方庫中甩出來的異常,我們也有方式可以截獲到。n***ception.m這個檔案中攜帶了乙個void nssetuncaughtexceptionhandler(nsuncaughtexceptionhandler * _nullable);的函式可以註冊乙個函式來處理未**獲的異常。雖然無法阻止程式崩潰,但是可以取得異常進行一些準備和後續處理,使用起來這樣:

1

2

3

4

5

6

7

8

9

void handleexception(n***ception *exception) 

nssetuncaughtexceptionhandler(&handleexception);

往往我們要做的,是把異常資訊儲存到本地,等到下次啟動的時候進行一些後續處理。這些就是crash收集工具所做的事兒。

從oc異常往底層走,我們看到的是mach異常。mach異常是freebsd上特有定義的高層異常,當然,現在網路上能收集到的資料都和mac和ios開發有關。相關的原始碼網路上可以找到這裡。看到異常定義的名稱我們會感覺到異常的親切——excmask開頭的異常呢。我們一一來總結常見的兩個mach異常吧:

exc_bad_access (bad memory access)

這是最常見並且我們覺得最頭疼的,記憶體訪問錯誤。這種異常分為兩種:

訪問物件未初始化(sigbus訊號)

訪問了什麼東西已經被**掉了(sigsegv訊號)

當然,事實上到底是怎樣的錯誤比上面描述的複雜神秘得多,這才是這個最難處理的主要原因。

exc_bad_access同時也提供了輔助的異常code來幫助我們判斷到底是什麼錯誤,比如kern_protection_failure是指的位址無許可權訪問,kern_invalid_address是指的位址不可用,異常資訊中還會包括具體出錯的位址。也許可以獲得更多的幫助呢。在debug執行是開啟記憶體管理的zombie objects可以獲得有效的除錯資訊。

exc_bad_instruction (illegal instruction)

通常通過sigill訊號觸發的異常,很明顯,它是在說執行了一條非法的指令。往往錯誤是這樣子的:

xc_bad_instruction (code=exc_i386_invop, subcode=0x0)

雖然是這樣說,都是編譯器編譯出來的指令怎麼會有非法指令嘛。所以事實上遇到這樣的問題往往是執行指令的引數不對,多半是為0即nil了。然後我們又回到了空指標的問題了~。

當然,除了**中的問題。更多的是ios開發中的玄學問題導致的ios本身異常和bug,比如這個就是這樣。解決這些問題,還是得老老實實的分析堆疊猜測和分析了。

其他其他在實際開發中有可能遇到的並不多,主要是:

exc_resource是指的程式到達資源上限,比如cpu占用過高,記憶體不足之類的。這樣的問題也沒法解決啦。

exc_guard是一些c層函式訪問錯誤導致的異常,比如fopen檔案訪問錯誤之類的都會爆出這個。不過我們好好的oc不用肯定一般也不會使用這些,所以還安好。

0x00000020,這些是被freebsd定義為玄學異常的異常都在裡面了,也提供了特殊的code來提供輔助資訊。其中其實最常見的code是0x8badf00d,是主線程阻塞時間太長,程式被os殺了。其他的遇到了就是見鬼了!

從mach異常再往上走追根究底,其實,所以異常發生的本質途徑都是unix的異常訊號。

oc異常並不是真正的異常,但是當乙個oc異常被丟擲到最外層還沒被誰捕獲,程式會強行傳送sigabrt訊號中斷程式。

mach異常沒有比較方便的捕獲方式,不過由於它本質就是訊號,所以這一段講的東西也能包含處理mach異常。

產生乙個不屬於mach異常的異常訊號也是非常非常簡單的事兒,比如:

1

2

int *i;

free(i);

總之,c層面,runtime或者其他東西控制程式就是通過訊號,中斷當然也不例外。通過不同的訊號,我們也能知道很多不同的東西。在ios開發環境中,訊號列舉在sys/signal.h檔案中,我們可以看到大量的unix訊號羅列其中,參考wiki可以看到各個訊號的詳解。當然,我們最終關心的是能否捕獲這些異常訊號來抓住異常和崩潰。對,方法是有的,這裡提供了乙個叫void (signal(int, void ()(int)))(int);的方法來註冊乙個處理函式。

這個方法最後吐出來的是當前的訊號,沒異常資訊堆疊怎麼辦,還好,從execinfo.h中,我們可以取出當然彙編層程式的堆疊情況。這就好辦了,最後處理**如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

void signalexceptionhandler(int signal) 

}

void installsignalhandler(void)

要注意的是這裡獲得的堆疊資訊是,當前彙編子程式的offset+指令offset,要麼我們需要符號表,要麼我們需要反編譯一些我們的程式來對應**了。

crash的實踐總結

大部分crash應該都是有空指標異常導致的 對於大部分的簡單的空指標異常,請相信編譯器。android studio中,對於大部分可能出現異常的情況,都會有相應的警告。請盡量處理編譯器的警告 大部分專案,應該eclipse中開發,可以轉換為android studio專案,或者將 複製到androi...

OC中的block的用法總結

1.block 的基本概念 了解 block是乙個型別,可以定義變數,它的變數是用來存 塊.2.block的基本用法 固定寫法牢記 block最簡單形式 定義格式 void block名 使用格式 block名 定義是,把block當初資料型別 特點 1,型別比函式定義多了乙個 2.設定數值,有乙個...

Java中的異常總結

異常分為檢查型異常 unchecked excepthon 和非檢查型異常 checkexception 非檢查型異常包括error和runtimeexception,其他所有異常都未檢查型異常。非檢查型異常 在編譯器不會主動檢查的異常型別,編寫過程中不要求開發者處理。這類異常一般可以避免,因此無需...