經營你的iOS應用日志(二):異常日志

如果你去4S店修車,給小工說你的車哪天怎么樣怎么樣了,小工有可能會立即搬出一臺電腦,插上行車電腦把日志打出來,然后告訴你你的車發生過什么故障。汽車尚且如此,何況移動互聯網應用呢。

本文第一篇:經營你的iOS應用日志(一):開始編寫日志組件

?

言歸正傳。開發iOS應用,解決Crash問題始終是一個難題。Crash分為兩種,一種是由EXC_BAD_ACCESS引起的,原因是訪問了不屬于本進程的內存地址,有可能是訪問已被釋放的內存;另一種是未被捕獲的Objective-C異常(NSException),導致程序向自身發送了SIGABRT信號而崩潰。其實對于未捕獲的Objective-C異常,我們是有辦法將它記錄下來的,如果日志記錄得當,能夠解決絕大部分崩潰的問題。這里對于UI線程與后臺線程分別說明。

先看UI線程。iOS SDK提供了NSSetUncaughtExceptionHandler函數,用法如:

NSSetUncaughtExceptionHandler( handleRootException );

這樣在UI線程發生未捕獲異常后,進程崩潰之前,handleRootException會被執行。這個函數實現如下

static void handleRootException( NSException* exception )
{
NSString* name = [ exception name ];
NSString* reason = [ exception reason ];
NSArray* symbols = [ exception callStackSymbols ]; // 異常發生時的調用棧
NSMutableString* strSymbols = [ [ NSMutableString alloc ] init ]; // 將調用棧拼成輸出日志的字符串
for ( NSString* item in symbols )
{
[ strSymbols appendString: item ];
[ strSymbols appendString: @"\r\n" ];
}

// 寫日志,級別為ERROR
writeCinLog( __FUNCTION__, CinLogLevelError, @"[ Uncaught Exception ]\r\nName: %@, Reason: %@\r\n[ Fe Symbols Start ]\r\n%@[ Fe Symbols End ]", name, reason, strSymbols );
[ strSymbols release ];

// 這兒必須Hold住當前線程,等待日志線程將日志成功輸出,當前線程再繼續運行
blockingFlushLogs( __FUNCTION__ );

// 寫一個文件,記錄此時此刻發生了異常。這個挺有用的哦
NSDictionary* dict = [ NSDictionary dictionaryWithObjectsAndKeys:
currentCinLogFileName(), @"LogFile", // 當前日志文件名稱
currentCinLogFileFullPath(), @"LogFileFullPath", // 當前日志文件全路徑
[ NSDate date ], @"TimeStamp", // 異常發生的時刻
nil ];
NSString* path = [ NSString stringWithFormat: @"%@/Documents/", NSHomeDirectory() ];
NSString* lastExceptionLog = [ NSString stringWithFormat: @"%@LastExceptionLog.txt", path ];
[ dict writeToFile: lastExceptionLog atomically: YES ];

}

而我們的日志組件必須實現blockingFlushLogs函數,確保進程在日志完全寫入文件后再退出。這個實現應該很簡單吧。

當應用下次啟動時,我們可以檢查,如果有LastExceptionLog.txt,則彈窗引導測試人員將日志發過來。如果iPhone上面配置了EMail帳戶,可以很簡單的調用MFMailComposeViewController將日志文件作為附件發送,當然也可以想其它辦法。

記得正式發布的版本要將它條件編譯去掉哦。

其中文件中的最后一條ERROR即為導致崩潰的異常,而從ERROR之前的日志可以看出當前程序的運行情況。ERROR如下:

<- 03-20 17:21:43 ERROR -> [UI] -[CinUIRunLoopActionManager(Protected) handleRootException:]
[ Uncaught Exception ]
Name: NSDestinationInvalidException, Reason: *** -[CinThreadRunLoopActionManager performSelector:onThread:withObject:waitUntilDone:modes:]: target thread exited while waiting for the perform
[ Fe Symbols Start ]
0 CoreFoundation 0x340c88d7 __exceptionPreprocess + 186
1 libobjc.A.dylib 0x343181e5 objc_exception_throw + 32
2 CoreFoundation 0x340c87b9 +[NSException raise:format:] + 0
3 CoreFoundation 0x340c87db +[NSException raise:format:] + 34
4 Foundation 0x35a12493 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 998
5 Foundation 0x35a3afb5 -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:] + 108
6 MyiOSapplication 0x0022b7e9 -[CinThreadRunLoopActionManager(Protected) performAction:] + 144
13 UIKit 0x374b36b5 -[UIViewController _setViewAppearState:isAnimating:] + 144
14 UIKit 0x374b38c1 -[UINavigationController viewWillAppear:] + 288
15 UIKit 0x374b36b5 -[UIViewController _setViewAppearState:isAnimating:] + 144
16 UIKit 0x3750e61b -[UIViewController beginAppearanceTransition:animated:] + 190
17 UIKit 0x3750b415 -[UITabBarController transitionFromViewController:toViewController:transition:shouldSetSelected:] + 184
18 UIKit 0x3750b357 -[UITabBarController transitionFromViewController:toViewController:] + 30
19 UIKit 0x3750ac91 -[UITabBarController _setSelectedViewController:] + 300
20 UIKit 0x3750a9c5 -[UITabBarController setSelectedIndex:] + 240
21 MyiOSapplication 0x0007ef1d +[Utility ResetCurrentTabIndex] + 172
22 MyiOSapplication 0x001a87bd -[UIViewController(statusBar) dismissModalViewControllerAnimatedEx:] + 416
23 MyiOSapplication 0x001793fb -[ImageProcessingViewController save:] + 690
24 CoreFoundation 0x34022435 -[NSObject performSelector:withObject:withObject:] + 52
25 UIKit 0x3748c9eb -[UIApplication sendAction:to:from:forEvent:] + 62
26 UIKit 0x3748c9a7 -[UIApplication sendAction:toTarget:fromSender:forEvent:] + 30
27 UIKit 0x3748c985 -[UIControl sendAction:to:forEvent:] + 44
28 UIKit 0x3748c6f5 -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 492
29 UIKit 0x3748d02d -[UIControl touchesEnded:withEvent:] + 476
30 UIKit 0x3748b50f -[UIWindow _sendTouchesForEvent:] + 318
31 UIKit 0x3748af01 -[UIWindow sendEvent:] + 380
32 UIKit 0x374714ed -[UIApplication sendEvent:] + 356
33 UIKit 0x37470d2d _UIApplicationHandleEvent + 5808
34 GraphicsServices 0x308a3df3 PurpleEventCallback + 882
35 CoreFoundation 0x3409c553 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 38
36 CoreFoundation 0x3409c4f5 __CFRunLoopDoSource1 + 140
37 CoreFoundation 0x3409b343 __CFRunLoopRun + 1370
38 CoreFoundation 0x3401e4dd CFRunLoopRunSpecific + 300
39 CoreFoundation 0x3401e3a5 CFRunLoopRunInMode + 104
40 GraphicsServices 0x308a2fcd GSEventRunModal + 156
41 UIKit 0x3749f743 UIApplicationMain + 1090
42 MyiOSapplication 0x000d4ccb main + 174
43 MyiOSapplication 0x000039c8 start + 40
[ Fe Symbols End ]

可以看到,即使我們沒有編譯時生成的符號文件,也能夠打印出調用棧上的每個函數的名稱,只是沒有文件名和行號。

那么,除了UI線程之外,自己創建的后臺線程呢?運行NSRunLoop的后臺線程的線程函數應該如下:

- ( void ) threadProc: ( NSString* )threadName
{
NSThread* current = [ NSThread currentThread ];
[ current setName: threadName ];
NSAutoreleasePool *pool = [ [ NSAutoreleasePool alloc ] init ];

// 一個沒有實際作用的NSTimer,確保NSRunLoop不退出。不知道有沒有更好的辦法啊
_dummyTimer = [ [ NSTimer timerWithTimeInterval: 10.0
target: self
selector: @selector( dummyTimerProc: )
userInfo: nil
repeats: YES ] retain ];

NSRunLoop *r = [ NSRunLoop currentRunLoop ];
[ r addTimer: _dummyTimer forMode: NSDefaultRunLoopMode ];
@try {
// 啟動后臺線程的NSRunLoop
[ r run ];
}
@catch ( NSException *exception ) {
[ self handleRootException: exception ];
// 一旦在線程根上捕捉到未知異常,記錄異常后本線程退出
}
@finally {
[ _dummyTimer invalidate ];
[ _dummyTimer release ];
[ pool release ];
}
}

后臺線程的handleRootException與UI線程基本一致。不過為了測試人員更加方便,其實只要不是UI線程發生未捕獲異常,都可以先引導用戶發送日志,再把進程崩潰掉。

?

轉載于:https://www.cnblogs.com/alario/archive/2012/03/28/2421574.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/377613.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/377613.shtml
英文地址,請注明出處:http://en.pswp.cn/news/377613.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Discuz 升級X3問題匯總整理

最近一段時間公司的社區垃圾帖數量陡然上漲&#xff0c;以至于社區首頁的推薦版塊滿滿都是垃圾帖的身影&#xff0c;為了進一步解決垃圾帖問題我們整整花了1天時間刪垃圾貼&#xff0c;清除不良用戶&#xff0c;刪的手都酸了&#xff0c;可見垃圾帖的數量之多&#xff01;可恥的…

【C++grammar】格式化輸出與I/O流函數

目錄1、格式化輸出1. setw manipulator(“設置域寬”控制符)2. setprecision manipulator(“設置浮點精度”控制符)3. setfill manipulator(“設置填充字符”控制符)4. Formatting Output in File Operation(在文件操作中格式化輸入/輸出)5.小練習2、用于輸入/輸出流的函數1. g…

python 忽略 異常_如何忽略Python中的異常?

python 忽略 異常什么是例外&#xff1f; (What is an Exception?) An exception is an event, which occurs during the execution of a program that interrupts the normal execution of the application. Generally, any application when encountered with a situation t…

三、實戰---爬取百度指定詞條所對應的結果頁面(一個簡單的頁面采集器)

在第一篇博文中也提及到User-Agent&#xff0c;表示請求載體的身份&#xff0c;也就是說明通過什么瀏覽器進行訪問服務器的&#xff0c;這一點很重要。 ① UA檢測 門戶網站服務器會檢測請求載體的身份。如果檢測到載體的身份表示為某一款瀏覽器的請求&#xff0c;則說明這是一…

Spring MVC攔截器實現分析

SpringMVC的攔截器不同于Spring的攔截器&#xff0c;SpringMVC具有統一的入口DispatcherServlet&#xff0c;所有的請求都通過DispatcherServlet&#xff0c;所以只需要在DispatcherServlet上做文章即可&#xff0c;DispatcherServlet也沒有代理&#xff0c;同時SpringMVC管理的…

碩士畢業后去國外讀法學博士_法學碩士的完整形式是什么?

碩士畢業后去國外讀法學博士法學碩士&#xff1a;豆科大法師(拉丁)/法學碩士 (LLM: Legum Magister (Latin)/ Master of Law) LLM is an abbreviation of Legum Magister. It is in term of Latin which states the masters degree of Law. In the majority, LLM is generally …

android:layout_weight屬性的簡單使用

效果&#xff1a; style.xml <style name"etStyle2"><item name"android:layout_width">match_parent</item><item name"android:layout_height">wrap_content</item><item name"android:background"…

一、環境配置安裝

一、Anaconda Ⅰ下載 最新版的anaconda可能會需要各種各樣的問題&#xff0c;python3.6版本比較穩定&#xff0c;建議使用。 老鐵們可以通過&#xff0c;Anaconda以前版本所自帶Python版本&#xff0c;查看Anaconda所帶的python版本 我用的是這個&#xff0c;Anaconda3-5.2.0…

leetcode 35. 搜索插入位置 思考分析

目錄題目暴力二分迭代二分遞歸題目 給定一個排序數組和一個目標值&#xff0c;在數組中找到目標值&#xff0c;并返回其索引。如果目標值不存在于數組中&#xff0c;返回它將會被按順序插入的位置。 你可以假設數組中無重復元素。 示例 1: 輸入: [1,3,5,6], 5 輸出: 2 示例 2:…

java優秀算法河內之塔_河內塔的Java程序

java優秀算法河內之塔Tower of Hanoi is a mathematical puzzle where we have three rods and n disks. The objective of the puzzle is to move all disks from source rod to destination rod using the third rod (say auxiliary). The rules are: 河內塔是一個數學難題&a…

轉——C# DataGridView控件 動態添加新行

DataGridView控件在實際應用中非常實用&#xff0c;特別需要表格顯示數據時。可以靜態綁定數據源&#xff0c;這樣就自動為DataGridView控件添加相應的行。假如需要動態為DataGridView控件添加新行&#xff0c;方法有很多種&#xff0c;下面簡單介紹如何為DataGridView控件動態…

分享通用基類庫-C#通用緩存類

1 /************************************************************************************* 2 * 代碼:吳蔣 3 * 時間:2012.03.30 4 * 說明:緩存公共基類 5 * 其他: 6 * 修改人&#xff1a; 7 * 修改時間&#xff1a; 8 * 修改說明&#xff1a; 9 ******************…

二、PyTorch加載數據

一、常用的兩個函數 dir()函數可以理解為打開某個包&#xff0c;help()可以理解為返回如何使用某個具體的方法 例如&#xff1a;若一個A錢包里面有a&#xff0c;b&#xff0c;c&#xff0c;d四個小包&#xff0c;則可通過dir(A)&#xff0c;打開該A錢包&#xff0c;返回a&…

leetcode 1005. K 次取反后最大化的數組和 思考分析

題目 給定一個整數數組 A&#xff0c;我們只能用以下方法修改該數組&#xff1a;我們選擇某個索引 i 并將 A[i] 替換為 -A[i]&#xff0c;然后總共重復這個過程 K 次。&#xff08;我們可以多次選擇同一個索引 i。&#xff09; 以這種方式修改數組后&#xff0c;返回數組可能…

三、TensorBoard

一、安裝TensorBoard 管理員身份運行Anaconda Prompt&#xff0c;進入自己的環境環境 conda activate y_pytorch&#xff0c;pip install tensorboard 進行下載&#xff0c;也可以通過conda install tensorboard進行下載。其實通俗點&#xff0c;pip相當于菜市場&#xff0c;c…

IT資產管理系統SQL版

你難道還在用Excel登記IT資產信息嗎&#xff1f; 那你一定要好好考慮如何面對以下問題 1&#xff1a;IT人員需要面對自身部門以下問題用戶申請了資產it部未處理的單還有哪些?庫存里面還有哪些資產?有多少設備在維修?有多少設備已經報廢了?哪些資產低于安全庫存需要采購?使…

詳細講解設計跳表的三個步驟(查找、插入、刪除)

目錄寫在前面跳表概要查找步驟插入步驟刪除步驟完整代碼寫在前面 關于跳表的一些知識可以參考這篇文章,最好是先看完這篇文章再看詳細的思路->代碼的復現步驟: Redis內部數據結構詳解(6)——skiplist 關于跳表的插入、刪除基本操作其實也就是鏈表的插入和刪除&#xff0c;所…

php 類靜態變量 和 常量消耗內存及時間對比

在對類執行100w次循環后&#xff0c; 常量最快&#xff0c;變量其次&#xff0c;靜態變量消耗時間最高 其中&#xff1a; 常量消耗&#xff1a;101.1739毫秒 變量消耗&#xff1a;2039.7689毫秒 靜態變量消耗&#xff1a;4084.8911毫秒 測試代碼&#xff1a; class Timer_profi…

一個機器周期 計算機_計算機科學組織| 機器周期

一個機器周期 計算機機器周期 (Machine Cycle) The cycle during which a machine language instruction is executed by the processor of the computer system is known as the machine cycle. If a program contains 10 machine language instruction, 10 separate machine …

四、Transforms

transform是torchvision下的一個.py文件&#xff0c;這個python文件中定義了很多的類和方法&#xff0c;主要實現對圖片進行一些變換操作 一、Transforms講解 from torchvision import transforms#按著Ctrl&#xff0c;點擊transforms進入到__init__.py文件中 from .transfo…