打算把《高性能iOS開發》這本書公開出來,供大家學習。這是第一章,感興趣的可以訂閱我的專題 高性能iOS應用開發。
本書假設你是 iOS 開發人員,有長期開發原生 iOS 應用的經驗,并且希望能夠從眾人中脫 穎而出,躋身于頂尖開發人員之列。
參考以下統計數據。
? 應用首次工作出錯以后, 79% 的用戶只會再重試一兩次。
? 當應用載入時間超過 3 秒時, 25% 的用戶會放棄使用該應用。
? 31% 的用戶會將糟糕的體驗轉告他人。
這些數據強調了性能對應用的重要性。應用能否被用戶所認可不僅僅取決于其功能,還取 決于當與用戶交互時,應用能否提供流暢的體驗。
幾乎完成任意特定任務的應用都能在 App Store 中找到大量的替代品。但用戶只會堅持使 用其中的某一款。被選中的這一款要么無可取代,要么極少出現故障且性能格外出眾。
性能會受許多重要因素所影響,這些因素包括內存消耗、網絡帶寬效率以及用戶界面的響 應速度。我們先概述不同類型的性能特征,然后再對它們進行測量。
1.1 定義性能
從技術視角嚴格來說,性能是非常模糊的術語。當一個人說“這是個高性能的應用”時, 其實我們無從判斷他說的是什么。他是說應用消耗的內存少?應用節約了網絡流量?還是 說應用使用起來非常流暢?總而言之,高性能有著多重的含義和豐富的解釋方式。
性能可以和我們后續討論的多個要點關聯起來。(需要測量和監控的)性能指標是其中的 一個關注點,(實際上收集數據的)測量是另一個關注點。
我們將在第 11 章深入探索測量的過程。提高工程參數的使用率是本書第二部分和第三部 分的重點難題。
1.2 性能指標
性能指標是面向用戶的各種屬性。每個屬性可能是一個或多個可測量工程參數的一個要素。
1.2.1 內存
內存涉及運行應用所需的 RAM 最小值,以及應用消耗的內存平均值和峰值。最小內存值 會嚴重限制硬件,而更高的內存平均值和峰值意味著更多的后臺應用會被強制關閉。
同時還要確保沒有泄漏內存。隨時間流逝而持續增長的內存消耗意味著,應用很可能會因 為內存不足的異常而崩潰。
我們會在第2章中對內存進行深入討論。
1.2.2 電量消耗
在編寫高性能代碼時,電量消耗是一個需要重點處理的重要因素。就執行時間和 CPU 資 源的利用而言,我們不僅要實現高效的數據結構和算法,還需要考慮其他的因素。如果某 個應用是個電池黑洞,那么一定不會有人喜歡它。
電量消耗不僅僅與計算 CPU 周期有關,還包括高效地使用硬件。除了要實現電量消耗最 小化,還要確保不會影響用戶體驗。
我們將在第 3 章討論這個問題。
1.2.3初始化時間
應用在啟動時應執行剛好夠用的任務以完成初始化,從而滿足用戶的使用需求。執行這些 任務消耗的時間就是應用的初始化時間。 剛好夠用是一個開放式用語——正確的平衡點取 決于應用的需要。
在首次使用應用時創建對象并進行初始化是一個合理的選擇,例如,直到需要使用對象時 才創建對象。這種方式被稱為惰性初始化。這是一種很好的策略,但也要考慮不能讓用戶 總是在執行后續任務時等待。
下面列舉了你可能想在應用初始化階段執行的一些動作,排名不分先后。
? 檢查應用是否為首次啟動。
? 檢查用戶是否已經登錄。
? 如果用戶已經登錄,盡可能地載入之前的狀態。
? 連接服務器以拉取最新的變更。
? 檢查應用是否由某個深層鏈接喚起。如果是,還需要載入深層鏈接相應的 UI 和狀態。
? 檢查是否存在應用上次啟動時掛起的任務,需要時恢復它們。 ? 初始化后續需要使用的對象和線程池。
? 初始化依賴項(如對象關系映射、崩潰報告系統和緩存)。
這個列表可能會迅速變長,并且很難決定哪些條目一定要在啟動時執行,哪些可以延后幾 毫秒再執行。
我們將在第 5 章探討這個問題。
1.2.4 執行速度
一旦啟動應用,用戶總是希望它可以盡可能快地工作。一切必要的處理都應該在盡可能短 的時間內完成。
例如,在照片應用中,用戶通常希望看到調整亮度或對比度等簡單效果的實時預覽效果。 因此,相應的處理需要在幾毫秒內完成。
這可能需要本地計算的并行處理技術或能夠將復雜任務分發到服務器。我們將在第 第 6 章介紹這些主題,并在第 7 章和第 11 章介紹相關工具。
1.2.5 響應速度
每個應用都應該快速地響應用戶交互。在應用中所做的一切優化和權衡最終都應該體現在 響應速度上。
App Store 中有許多應用可以完成相似或相關的任務。這為用戶提供了很大的選擇空間,而 用戶基本都會選擇響應最快的應用。
第 4 章介紹了用并行處理技術優化本地執行。第 5 章和第 6 章介紹了實現流暢交互的最佳 實踐。第 10 章將探索如何對應用進行測試。
1.2.6 本地存儲
針對任何在服務器上存儲數據或通過外部來源刷新數據的應用,開發人員應該對本地存儲 的使用有所規劃,以便應用具備離線瀏覽的能力。
例如,用戶都希望郵件應用能夠在無網絡或設備離線的情況下瀏覽歷史郵件。
同樣,新聞應用也應該可以在離線模式下顯示最近更新的新聞,并標記出每條新聞是否已讀。
然而,從本地存儲中載入和同步數據應該迅速、便捷。這不僅需要選擇要在本地緩存的數 據和要優化的數據結構,還需要提供一系列的配置選項并確定數據同步的頻率。
如果你的應用使用了本地存儲,那么請提供一個清除數據的選項。遺憾的是,市場上的大 部分應用都沒有提供此選項。更讓人煩惱的是,一些應用竟然會消耗數百兆的存儲空間。 用戶會頻繁地卸載這些應用來回收本地存儲。這會導致糟糕的用戶體驗,從而威脅應用的 成功。
如圖 1-1 所示,超過 12GB 的空間已經被使用了,留給用戶的內存還有 950MB。 其實,大 部分的數據可以安全地從本地刪除。這些應用應該提供清理緩存的選項。
一定要向終端用戶提供清空本地緩存的選項。 如果用戶開啟了 iCloud 的備份功能,那么應用的數據將會消耗用戶的存儲限 額,請謹慎使用。
第7章、第8章和第9章會介紹本地存儲相關的話題。
1.2.7 互操作性
用戶可能會使用多個應用來完成某個任務,這就需要這些應用直接提供互操作的能力。例 如,一個相冊可能需要一個幻燈片應用來實現最佳的瀏覽體驗,但需要另一個應用來編輯 照片。其中瀏覽照片的應用要能夠將照片發送到編輯器,并接收編輯后的圖片。
iOS 為實現應用間的互操作和數據共享提供了多種機制,其中包括 UIActivityViewController 、 深層鏈接、 MultipeerConnectivity 框架,等等。
為深層鏈接定義良好的 URL 結構與編寫優異的代碼來解析 URL 同樣重要。類似地,使用 共享對話框共享數據時,精確識別用于分享的數據非常重要,同時,在處理不同數據源傳 入的數據時還要注意安全隱患。
如果某個應用向附近設備共享數據時需要花費很長時間準備數據,那么用戶體驗就會非常 糟糕。
我們會在第 8 章中討論這些內容。
1.2.8 網絡環境
移動設備會在不同網絡環境下使用。為了確保能夠提供最好的用戶體驗,你的應用應當適 應各種網絡條件:
? 高帶寬穩定網絡
? 低帶寬穩定網絡
? 高帶寬不穩定網絡
? 低帶寬不穩定網絡
? 無網絡
為用戶提供進度指示或錯誤信息是相對合理的方式,無盡的等待或崩潰則讓人無法接受。信息流大小, 以此告訴終端用戶還需要等待多久才可以播放音樂。 MoneyControl 和 Bank圖 的屏幕截圖展示了向終端用戶傳遞信息的不同方式。 應用顯示了已經緩沖的of America 等其他應用僅提供了不明確的進度條,這在非流式應用中是更為常見的樣式。
我們將在第7章中深入探討此話題。
1.2.9 帶寬
人們會在不同的網絡條件下使用自己的移動設備,網速從每秒數千字節到每秒數十兆字節。
因此,帶寬的優化使用是定義應用質量的另一個關鍵參數。此外,在高帶寬網絡下運行一 個基于低帶寬網絡開發的應用可能會產生完全不同的結果。
2010 年左右,我和我的團隊正在印度開發一款應用。由于處于低帶寬網絡,應用的本地初 始化速度要比從服務器端載入資源快得多,于是我們針對這種情況進行了優化。
然而,當這款應用投入韓國市場時,我們對它進行了測試,結果卻讓人大跌眼鏡。之前所 進行的所有優化幾乎毫無意義,我們不得不重寫了大部分可能導致資源和數據沖突的相關 代碼。
為提高性能所做的設計并非每次都能如愿,也可能會導致相反的效果。
第 7 章包含了優化使用帶寬的最佳實踐。
1.2.10 數據刷新
即使沒有提供離線瀏覽能力,你仍然可以從服務器端周期性地刷新數據。刷新的頻率和每 次傳輸的數據量將決定數據傳輸的總量。如果傳輸的字節數過大,那用戶必然會快速耗盡 自己的流量計劃。當流量消耗大到一定程度時,你的應用很可能會流失用戶。
在 iOS 6.x 或更低版本中,在后臺運行的應用不能刷新數據。從 iOS 7 開始,應用可以在后 臺周期性地刷新數據。對于在線聊天類應用,持久的 HTTP 連接或原生 TCP 連接可能會非 常有用。
第5章和第7章會介紹這部分內容。
1.2.11多用戶支持
家庭成員間可能會共享移動設備,或者一個用戶可能會擁有同一應用的多個賬號。例如, 兄弟姐妹間可能會共享一個 iPad 來玩游戲。再比如,家庭成員可能會在旅游時配置一個設 備來查收全家人的電子郵件,以減少漫游費用,尤其是在境外旅游時。類似地,一個人也 可能會配置多個電子郵件賬號。
是否支持多個并發用戶取決于產品的需要。一旦決定提供此類功能,請參考以下準則。
? 添加新用戶應盡可能高效。
? 在不同用戶之間更新應盡可能高效。
? 在不同用戶之間切換應盡可能高效。
? 用戶數據的界限應該簡潔且沒有 bug。
圖 展示了兩個提供了多用戶支持的應用。 左邊和右邊分別展示了 Google 和 Yahoo 應1-3 用的賬號選擇功能。
第9章將介紹如何在應用支持多用戶的同時保障安全以及其他內容。
1.2.12 單點登錄
如果你已經創建了多個允許或需要登錄的應用,那么支持單點登錄(single sign-on,SSO) 是非常棒的選擇。 如果用戶登錄了一個應用, 只需要點擊一次, 就可以登錄到其他的應 用中。
這個過程不僅需要支持跨應用的數據共享,還需要分享狀態、跨應用同步等。例如,如果 用戶注銷了其中某個應用,則通過 SSO 登錄的所有其他應用也應能注銷掉。
此外,應用之間的同步應該是安全的。
第 9 章將會介紹這部分內容。
1.2.13 安全
安全對移動應用來說是最重要的,因為敏感信息可能會在應用間共享。因此,對所有通信 以及本地數據和共享數據進行加密就顯得尤為重要了。
實現安全需要更多的計算、內存和存儲,但這與最大化運行速度、最小化內存和存儲使用 的目標相沖突。
因此,你需要在安全和其他因素之間進行權衡。
引入多個安全層會影響性能,并對用戶體驗造成可感知的負面影響。如何設定安全的基線 需要參考對用戶群體的統計分析。此外,硬件在其中扮演了重要的角色:選擇會因為不同 設備的計算能力而有所不同。
第 9 章將會深入介紹安全。
1.2.14崩潰
應用可能會而且確實會崩潰。過度優化會導致崩潰。同樣,使用原始 C 代碼也可能會導致 崩潰。
高性能的應用不僅應盡可能地避免崩潰,還應該在崩潰發生時優雅地恢復,尤其是在進行 某個操作的過程中發生崩潰時。
第12章會深入討論崩潰報告、檢測和分析。
1.3 應用性能分析
我們在前面討論過一些參數,通過測量它們來分析應用的方式有兩種:采樣和埋點。接下 來我們將逐一介紹。
1.3.1 采樣
顧名思義,采樣(或基于探測點的性能分析)是指以一定的周期間隔采集狀態,這通常需 要借助工具。我們將在 11.2 節中介紹這些工具。由于不會干擾應用的執行,因此采樣可以 很好地提供應用的全景圖。采樣的不足之處在于它不能返回 100% 精確的細節。如果采樣 的頻率是 10 毫秒,那么你就無法得知在探測點之間的 9.999 毫秒內發生了什么。
采樣可以作為初始的性能調研手段,并可用于跟蹤 CPU 和內存的使用情況。
1.3.2 埋點
通過修改代碼,記錄細節信息的埋點能夠提供比采樣更加精確的結果。你既可以在關鍵部 分主動埋點,也可以在性能分析或處理用戶反饋時有針對性地埋點,以便解決問題。 1.4.3 節將深入討論這一過程。
因為埋點需要注入額外代碼,所以它一定會影響應用的性能,對內存或速度 (或同時對二者)造成損害。
1.4 測量
現在,我們已經確定了需要測量的參數,并且研究了測量所需要的不同類型的分析。我們 先簡單了解一下如何實現測量。
通過測量性能并找出真正存在問題的地方,你可以避免掉入過早優化的陷阱。高德納曾經 這樣描述過早優化:
真正的問題在于,程序開發人員為提升程序效率在錯誤的方向和時間點浪費了太 多時間;過早優化是編程領域的萬惡(至少是絕大多數的惡)之源。 2
1.4.1 設置工程與代碼
接下來,我們將建立一個工程,以便在開發和生產階段測量已經定義好的參數。針對工程 配置、安裝和代碼實現共有三類任務。
?構建與發布
確保能夠輕松地構建和發布應用。
?可測試性
確保你的代碼能夠同時在模擬數據和真實數據之上工作,其中包括能夠模擬真實場景的 隔離環境。
?可跟蹤性
確保你能夠通過明確問題發生的位置和代碼行為來處理錯誤。
接下來將逐一討論這些設置項。
1. 構建與發布
構建和發布是直到最近才出現的話題。好在由于對靈活和敏捷的強烈需求,系統和工具得 到了改進。改進后的系統和工具現在可以加速拉取依賴信息,加速構建和發布用于測試或 企業分發的產品,也可以為公眾發布而提高提交文件到 iTunes Connect 的速度。
2000 年, Joel Spolsky 在其發表的一篇博文(http://www.joelonsoftware.com/articles/fog0000000043. html)中提出了一個問題: “你能(從源碼)一鍵構建自己的應用嗎?”這個問題現在依 然成立, 且問題的答案很可能會決定你在發現缺陷或瓶頸后改進質量和解決性能問題的 速度。
基于 Ruby 語言實現的 CocoaPods(https://cocoapods.org)實際上是 Objective-C 和 程的依賴管理器。 3 CocoaPods 與 Xcode 命令行工具相集成,可用于構建與發布。
2. 可測試性
每個應用都包含多個協同工作的組件。一個設計良好的系統應該遵循低耦合和高內聚,并允許替換任意或全部組件的依賴。
可以通過模擬依賴項目對每個組件進行隔離測試。一般來說,測試有兩種類型。
? 單元測試
驗證每個代碼單元在隔離環境下的操作。常見的做法是,在特定的環境中用不同的輸入 數據反復地調用一些方法,以評估代碼的表現。
?功能測試
驗證組件在最終集成的安裝包中的操作。可以在軟件的最終發布版本中驗證,也可以在 某個為測試而構建的參考應用中驗證。
我們將在第 10 章中深入討論測試。
3. 可跟蹤性
在開發階段,埋點可以幫助我們確定性能優化的優先級、提高對問題現場的還原能力,并 提供更多的調試信息。崩潰報告專注于從軟件的產品版本中收集調試信息。
1.4.2 設置崩潰報告
崩潰報告系統收集用于分析應用的調試日志。市面上有數十種崩潰報告系統。本書選用了 Flurry(http://www.furry.com) ,但這并不代表我對其他系統有任何偏見。選用 Flurry 的主要 原因是,只用一個 SDK 就可以同時實現崩潰報告和埋點。我們將在第 12 章深入介紹埋點。
要想使用 Flurry, 需要在 www.furry.com 中建立一個賬戶,得到一個 API 密鑰,然后下載 并設置 Flurry SDK。 例 1-1 展示了初始化 Flurry 的代碼。
例 1-1 在應用委托中配置崩潰報告
#import "Flurry.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[Flurry setCrashReportingEnabled:YES];
[Flurry startSession:@"API_KEY"];//用賬戶關聯的API密鑰(可以在Flurry的Dashboard中找到)替換 API_KEY 。
}
崩潰報告系統會用 NSSetUncaughtExceptionHandler 方法設置全局的異常處 理器。使用自定義的處理器則會失效。
如果希望繼續使用自己的處理器,那么你需要在崩潰報告系統初始化之后再 進行設置。此外,還可以通過 NSGetUncaughtExceptionHandler 方法得到崩 潰報告系統所設置的處理器。
1.4.3 對應用埋點
對應用進行埋點是了解用戶行為的一個重要步驟, 但更重要的目的是識別應用的關鍵路 徑。注入特定的代碼以記錄關鍵指標是提升應用性能的重要步驟。
對依賴進行抽象化和封裝是個好主意。這樣就可以在最后再進行切換,甚至 可以在作出最終決定之前同時使用 多個系統 。這在項目處于評估階段且存在 多個備選方案時尤為有用。
如例1-2 所示, 我們將增加一個名為 HPInstrumentation 的類來封裝埋點。 現在, 我們用 NSLog 登錄控制臺,并向服務器發送細節。
例1-2 HPInstrumentation 類封裝了埋點 SDK
//HPInstrumentation.h
@interface HPInstrumentation: NSObject
+(void)logEvent:(NSString *)name; +(void)logEvent:(NSString *)name withParameters:(NSDictionary *)parameters;
@end
//HPInstrumentation.m
@implementation HPInstrumentation
+(void)logEvent:(NSString *)name { NSLog(@"%@", name); [Flurry logEvent:name]; }
+(void)logEvent:(NSString *)name withParameters:(NSDictionary *)parameters { NSLog(@"%@ -> %@", name, params); [Flurry logEvent:name withParameters:parameters]; }
@end
先在應用生命周期的三個關鍵階段進行埋點(見例1-3) :
? 每當應用進入前臺, applicationDidBecomeActive: 方法會被調用
? 每當應用進入后臺, applicationDidEnterBackground: 方法會被調用
? 如果應用收到低內存警告, applicationDidReceiveMemoryWarning: 方法會被調用
出于娛樂的目的,我們在HPFirstViewController 中添加一個按鈕,點擊該按鈕將導致應用崩潰。
例1-3 在應用委托中的基礎埋點
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[HPInstrumentation logEvent:@"App Activated"];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[HPInstrumentation logEvent:@"App Backgrounded"]; } - (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[HPInstrumentation logEvent:@"App Memory Warning"];
}
我們將這些事件分別命名為 App Activated 、 App Backgrounded 和 App Memory Warning 。你 可以選擇自己喜歡的任意名稱,也可以使用數字。
埋點不應該取代日志。日志可以非常詳細。但因為向服務器報告時會消耗網 絡資源,所以你應該盡可能少地埋點。
因此,只對你和其他工程或產品團隊的成員感興趣的事件進行埋點是非常重 要的。(這些事件要包含足夠多的數據以滿足重要報告的需要。) 埋點和過度埋點之間并沒有清晰的分界線。 一開始應僅對少量報告進行埋 點,然后隨著時間的推進逐步增加埋點的覆蓋率。
接下來添加一個UI控件,讓它能夠觸發一個崩潰,這樣我們就可以看到崩潰報告。
圖 1-4 展示了崩潰按鈕的 UI。 例 1-4 展示了連接 Touch Up Inside 事件和 crashButtonWasClicked: 方法的代碼。
例 1-4 拋出異常使得應用崩潰
- (IBAction)crashButtonWasClicked:(id)sender
{
[NSException raise:@"Crash Button Was Clicked" format:@""];
}
讓我們與應用進行交互,從而產生一些事件。
(1) 安裝并運行應用。
(2) 將應用切換到后臺。
(3) 將應用切換到前臺。
(4) 多次重復第 2 步和第 3 步。
(5) 點擊 Generate Crash 按鈕,導致應用崩潰。
(6) 再次運行應用。直到此時崩潰報告才會實際發送到服務器。
第一批埋點事件和崩潰報告會在稍后上報到服務器并得到處理。報告可能在一段時間后才 能在Flurry 的 Dashboard中出現。 然后你可以進入 Dashboard 查看這些事件和崩潰報告。你應該會看到與下面截圖類似的內容,以下截圖是為我的應用從 Dashboard 中截取的。
圖1-5展示了用戶會話,用戶會話指的是有多少用戶在一天中至少啟動了應用一次。多次運行可能被認定是同一個會話,也可能不會被認定是同一個會話,這主要取決于多次運行 之間的時間間隔。
圖 展示了每個埋點事件的詳細崩潰情況。這份報告更加有用,因為它可以讓我們深入了解應用的使用率。比如,它精確地指出了應用中的哪些部分比其他部分使用頻度更高。
如果查看圖 1-7 中的崩潰報告,你會注意到其中的download鏈接,這個鏈接可以下載崩潰日志。點擊鏈接,下載日志。看著很熟悉是嗎?
在iTunes Connect中查看崩潰報告
Apple 提供了下載崩潰報告的服務。你可以利用該服務下載 TestFlight 或 App Store 所 發布的應用的最近版本,也可以構建相應的崩潰報告。理論上來說,有了這個服務就 不再需要第三方的崩潰報告工具了。
但有一個棘手的問題。除非用戶同意與開發人員分享崩潰數據,否則崩潰日志不會發 送至 Apple。TestFlight 用戶自動同意了分享崩潰數據。但產品應用(通過 App Store 分發的應用)必須由用戶開啟分享。
要想實現這一點, 用戶需要進入設置應用, 打開“隱私→診斷與用量”(Privacy → Diagnostics & Usage) ,然后選擇“自動發送”(Automatically Send)選項(見圖 1-8) 。
這里存在兩個問題。首先,用戶不能在你的應用內設置這個選項,他們需要進入設置 應用并進入特定的設置項。第二點更為重要,這項設置會對所有的應用生效:用戶無 法只為特定的應用發送崩潰報告。
使用第三方的崩潰報告工具可以確保你能夠控制整體體驗和用戶設置,以便向服務器 發送崩潰報告。
1.4.4 日志
日志是無價之寶,可以用于了解應用發生了什么事。
日志和埋點之間存在著細微的差別。埋點可以看作日志的子集。被埋點的任何數據都應該 記錄在日志中。
埋點承擔了為聚合分析發布關鍵性能數據的職責,日志則提供了用于在不同級別跟蹤應用 的細節信息,比如 debug 、 Verbose 、 info 、 warning 和 Error 。日志的記錄會貫穿應用的整 個生命周期,而埋點只應該用在開發的特定階段。
埋點數據會發送到服務器,日志是記錄在設備本地。
就日志而言,我們可以通過 CocoaPods 引入 CocoaLumberjack 來使用。
例 1-5 展示了添加到 Pod?le 的一行代碼,以便引入庫。完成相關改動后,運行 pod update 以更新 Xode 的工作空間。
例 1-5 為 CocoaLumberjack 配置 Pod?le
pod 'CocoaLumberjack', '~> 2.0'
CocoaLumberjack 是一個擴展性很強的框架,捆綁了一系列內置的日志記錄器,這些記錄器 可以向不同的目標發送信息。 例如, 使用 DDASLLogger 可以向 Apple System Log(ASL, NSLog 方法的默認位置)記錄日志。類似地,使用 DDFileLogger 可以向文件記錄日志。可 以在應用運行期間配置記錄器。
DDLog<Level> 宏指令可以用于記錄某個特定層級的日志。層級越高,信息越重要。最高級 別是 Error ,最低級別是 Verbose 。實際記錄消息的最低層級可以配置在每個文件層級、每 個 配置層級、每個日志器層級或全局。
以下的宏指令可供使用。
? DDLogError
表示不可恢復的錯誤。
? DDLogWarn
表示可恢復的錯誤。
? DDLogInfo
表示非錯誤的信息。
? DDLogDebug
表示數據主要用于調試。
? DDLogVerbose
幾乎提供了所有的細節,主要用于跟蹤執行過程中的控制流。
這些宏指令有著與 NSLog 相同的簽名。這意味著你可以直接用適合的 DDLog<Level> 調用來 取代 NSLog 。
例 1-6 展示了配置和使用這個庫的代表性代碼。
例 1-6 配置和使用 CocoaLumberjack
//設置
-(void)setupLogger {//最有可能在 application:didFinishLaunchingWithOptions: 調用這個方法。
#if _DEBUG
[DDLog addLogger:[DDASLLogger sharedInstance]]; //當連接到 Xcode 時,只在調試模式下將日志記錄到 ASL。 你不會希望這類日志在產品 環境的設備中記錄下來。
#endif
DDFileLogger fileLogger = [[DDFileLogger alloc] init]; //文件日志記錄器, 配置為每 24 小時( rollingFrequence )創建一個新文件, 同時最多 允許創建 個文件( maximumNumberOfLogFiles )。
fileLogger.rollingFrequency = 60 * 60 * 24;
fileLogger.logFileManager.maximumNumberOfLogFiles = 7;
[DDLog addLogger:fileLogger]; //注冊日志記錄器。
} //在一些文件中使用記錄器
#if _DEBUG //將日志的級別( ddLogLevel )設置為合適的值。這里我們可以這樣設置:開發階段輸出 最多的細節;內部測試階段( MY_INTERNAL_RELEASE 是一個自定義的標記)輸出少量細 節信息( debug level );面向終端用戶的分發包只輸出錯誤信息。
static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
#elsif MY_INTERNAL_RELEASE
static const DDLogLevel ddLogLevel = DDLogLevelDebug; #else
static const DDLogLevel ddLogLevel = DDLogLevelWarn;
#end -(void)someMethod {
DDVerbose(@"someMethod has started execution");//記錄一些信息。在 DDLogLevelVerbose 級別中,所有信息都會被記錄;在 DDLogLevelWarn 級別中,只有錯誤信息會被記錄。
//...
DDError(@"Ouch! Error state. Don't know what to do");
//...
DDVerbose(@"someMethod has reached its end state");
}
建議在應用委托的 application:didFinishLaunchingWithOptions: 回調中 調用此方法。
1.5 小結
我們在本章了解了影響應用性能的因素。性能不僅涉及用戶體驗,更關系到應用能否高效 運行。
我們查看了一些影響應用性能的關鍵屬性。在包含測量和追蹤的各項指標中,這些屬性成 為了性能的關鍵指示器。
我們討論了性能分析的概念,并寬泛地介紹了兩類分析技術:采樣與埋點。還介紹了一些 代碼改動,以便對應用進行埋點。然后我們通過使用被埋點的應用來觸發事件。
最后,我們在類中添加一些樣板代碼以幫助進行埋點和記錄日志。
第二部分的章節主要關注定義性能的每個屬性。每章都從定義和評審屬性開始,然后討論 一些潛在的問題,并用實際的代碼來解決這些問題。