iOS 效能調試,ios效能調試

來源:互聯網
上載者:User

iOS 效能調試,ios效能調試
效能調優的方式:

 1、通過專門的效能調優工具

 2、通過代碼最佳化

1. 效能調優工具:

下面針對iOS的效能調優工具進行一個介紹:

1.1 靜態分析工具–Analyze

相信iOS開發人員在App進行Build或Archive時,會產生很多編譯警告,這些警告是編譯時間產生的,靜態分析的過程也類似,在XCode Product菜單下,點擊Analyze對App進行靜態分析。

Analyze主要分析以下四種問題:

   1、邏輯錯誤:訪問null 指標或未初始化的變數等;

  2、記憶體管理錯誤:如記憶體流失;

  3、聲明錯誤:從未使用的變數;

  4、API調用錯誤:未包含使用的庫和架構。

1.2 記憶體流失分析工具–Leaks

點擊XCode的Product菜單Profile啟動Instruments,使用Leaks開始動態分析。
選擇Leaks,會自動啟動Leaks工具和IOS模擬器,Leaks啟動後會開始錄製,隨著對模擬器啟動並執行App的操作,可以在Leaks中查看記憶體佔用的情況。

  註:如果項目使用了ARC,隨著操作,不斷地開啟或關閉視圖,記憶體可能持續上升,但這不一定表示存在記憶體流失,ARC釋放的時機是不固定的。

Leaks頂部分為兩欄:Allocations和Leaks,右側的曲線代表記憶體配置和記憶體流失曲線。

點擊第二欄Leaks,進行記憶體流失分析,左下角會出現Leaks調試的選項:

 建議把Snapshot Interval間隔時間設定為10秒,勾選Automatic Snapshotting,Leaks會自動進行記憶體捕捉分析。

在你懷疑有記憶體流失的操作前和操作後,可以點擊Snapshot Now進行手動捕捉。<喎�"/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPtLUz8LKx8fQu7u1vc7StcRBcHDW0LX308MgPGNvZGU+KyhVSUltYWdlKilnZXRTdWJJbWFnZToodW5zaWduZWQgbG9uZyl1bFVzZXJIZWFkZXI8L2NvZGU+ILqvyv21xMrTzbyjrL/J0tS3os/WxNq05tC5wqmjujxiciAvPg0KPGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20160429/20160429090352437.png" title="\" />

Leaked Object的表格中顯示了記憶體流失的類型、數量及記憶體空間。

點擊具體的某個記憶體流失對象,在右側Detail視窗中會出現導致泄漏可能的位置,其中黑色頭像代表了最可能的位置。

Leaks已成功找出了[CMTool getSubImage:]這個函數:

 記憶體流失動態分析技巧

  熟練使用Leaks後會對記憶體流失判斷更準確,在可能導致泄漏的操作裡,多使用Snapshot Now手動捕捉。

  開始時如果裝置效能較好,可以把自動貼齊間隔設定為5秒鐘。

  使用ARC的項目,一般記憶體流失都是malloc、自訂結構、資源引起的,多注意這些地方進行分析。

開啟ARC後,記憶體流失的原因

  開啟了ARC並不是就不會存在記憶體問題,蘋果有句名言:ARC is only for NSObject。

  在IOS 中使用malloc分配的記憶體,ARC是不會處理的,需要自己進行處理。

  例子中的 CGImageRef 也是一個Image的指標,ARC也不會進行處理。

1.3 不合理記憶體分析工具–Allocation

關於記憶體的問題,除了記憶體流失以外,還可能存在記憶體不合理使用的情況,也會導致IOS記憶體警告。

記憶體的不合理使用往往比記憶體流失更難發現,記憶體流失可以更多藉助於工具的判斷,而記憶體的不合理運用更多需要開發人員結合代碼、架構來進行分析。

明確說明一下兩者的區別:

  記憶體流失:指記憶體被分配了,但是程式中已經沒有志向該記憶體的指標,導致該記憶體無法被釋放,一直佔用著記憶體,產生記憶體流失。

   記憶體不合理運用:蘋果官方稱這種情況為abandoned memory,也就是存在已指派記憶體的引用,但實際上程式中並不會使用,比片等對象進行了緩衝,但是緩衝中的對象一直沒有被使用。

XCode提供的Instruments中的Allocation工具可以用來幫你瞭解記憶體的分配情況,當你的App收到記憶體警告時,首先應該用Allocation進行記憶體分析,瞭解哪些對象佔用了太多記憶體。

1.4 幹掉殭屍對象–Zombies

殭屍對象,也就是我們會遇到的EXC_BAD_ACCESS錯誤,由於記憶體已經被釋放,而這個對象仍舊保留這那個壞地址而導致的。
在MRC的開發中,這個錯誤比較常見,ARC下面在使用到C++的代碼也會遇到。
不過這個工具比較簡單,遇到這類錯誤,開啟這個位於Instruments下的工具,直接就能幫你定位到,這裡就不贅述了。

1.5 效能提升工具–Time Profile

這篇文章的重中之重!

既然是效能調優,那麼怎麼提升代碼的運行效率其實才是我們程式員最直接的訴求,而這個工具可以輔助我們辦到這件事。

Time Profiler分析原理: 它按照固定的時間間隔來跟蹤每一個線程的堆棧資訊,通過統計比較時間間隔之間的堆棧狀態,來推算某個方法執行了多久,並獲得一個近似值。它將各個方法消耗的時間統計起來,形成了我們直接定位需要進行最佳化的代碼的好幫手。

選擇Time Profiler工具開始測試,這時會自動啟動模擬器和Time Profiler錄製。

先進行一些App的操作,讓Time Profiler收集足夠的資料,尤其是你覺得那些有效能瓶頸的地方。

 4、5、6所標記的面板 需要關注的:

  4是擴充面板,用來跟蹤顯示堆棧;

  5是詳細地面板,可以從這裡看到cpu啟動並執行時間都消耗在哪裡;

  6是選項面板,可以用來設定Time Profiler的運行參數。

通過對應用的操作,可以在詳細面板中看到那些最耗時的操作是哪些,並可以逐行展開查看:

表徵圖為黑色頭像的就是Time Profiler給我們的提示,有可能存在效能瓶頸的地方,可以逐漸向下展開,找到產生的根本原因。

Time Profiler參數設定

這裡邊幾個選項的含義如下:

  Separate by Thread: 每個線程應該分開考慮。只有這樣你才能揪出那些大量佔用CPU的”重”線程

  Invert Call Tree: 從上倒下跟蹤堆棧,這意味著你看到的表中的方法,將已從第0幀開始取樣,這通常你是想要的,只有這樣你才能看到CPU中話費時間最深的方法.也就是說FuncA{FunB{FunC}} 勾選此項後堆棧以C->B-A 把調用層級最深的C顯示在最外面

  Hide Missing Symbols: 如果dSYM無法找到你的app或者系統架構的話,那麼表中看不到方法名只能看到十六進位的數值,如果勾線此項可以隱藏這些符號,便於簡化資料

  Hide System Libraries: 勾選此項你會顯示你app的代碼,這是非常有用的. 因為通常你只關心cpu花在自己代碼上的時間不是系統上的

  Show Obj-C Only: 只顯示oc代碼 ,如果你的程式是像OpenGl這樣的程式,不要勾選側向因為他有可能是C++的

  Flatten Recursion: 遞迴函式, 每個堆疊追蹤一個條目

  Top Functions: 一個函數花費的時間直接在該函數中的總和,以及在函數調用該函數所花費的時間的總時間。因此,如果函數A調用B,那麼A的時間報告在A花費的時間加上B花費的時間,這非常真有用,因為它可以讓你每次下到呼叫堆疊時挑最大的時間數字,歸零在你最耗時的方法。

上面的參數在實踐中合理設定,也沒有什麼太多技巧,就是通過資料的隱藏、顯示讓我們更關注於想找到的資料。

2. 效能調優的代碼最佳化:

下面介紹一下在開發中可以直接進行的代碼最佳化的方面。

2.1 views設定為不透明(opaque=yes)

(opaque)這個屬性給渲染系統提供了一個如何處理這個view的提示。如果設為YES, 渲染系統就認為這個view是完全不透明的,這使得渲染系統最佳化一些渲染過程和提高效能。

如果設定為NO,渲染系統正常地和其它內容組成這個View。預設值是YES。

給你們看個圖,你們就知道了,如果這個屬性為NO,那麼:

知道了吧,GPU會利用圖層顏色合成公式去合成真正的色值,這在ScrollView或者動畫中是很消耗效能的。

2.2 不要阻塞主線程

永遠不要使主線程承擔過多。因為UIKit在主線程上做所有工作,渲染,管理觸摸反應,回應輸入等都需要在它上面完成。

一直使用主線程的風險就是如果你的代碼真的block了主線程,你的app會失去反應。

大部分阻礙主進程的情形是你的app在做一些牽涉到讀寫外部資源的I/O操作,比如儲存或者網路。

通常建議這些操作都使用GCD的方式直接非同步執行,並將UI相關操作在主線程進行回調,像這樣:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    // 切換到全域隊列,非同步執行耗時操作    dispatch_async(dispatch_get_main_queue(), ^{        // 切換到主線程,更新你的UI。    });});
2.3 提前調整ImageView中的圖片大小(同圖片和動畫的渲染)

如果要在UIImageView中顯示一個圖片,你應保證圖片的大小和UIImageView的大小相同。
因為在運行中縮放圖片是很耗費資源的,特別是UIImageView嵌套在UIScrollView中的情況下。

如果圖片是從遠端服務載入的你不能控製圖片大小,比如在下載前調整到合適大小的話,你可以在下載完成後,最好是用background thread,縮放一次,然後在UIImageView中使用縮放後的圖片。

這個類比到圖片和動畫的渲染中,是通用的。

具體方法參考上面的GCD操作。

2.4 正確使用容器的特性

Arrays: 有序的一組值。使用index來尋找很快,使用value 尋找很慢, 插入/刪除很慢。 Dictionaries: 儲存索引值對。 用鍵來尋找比較快。 Sets: 無序的一組值。用值來尋找很快,插入/刪除很快。

2.5 大檔案傳輸使用gzip

大量app依賴於遠端資源和第三方API,你可能會開發一個需要從遠端下載XML, JSON, HTML或者其它格式的app。

問題是我們的目標是行動裝置,因此你就不能指望網路狀況有多好。一個使用者現在還在edge網路,下一分鐘可能就切換到了3G。不論什麼情境,你肯定不想讓你的使用者等太長時間。

減小文檔的一個方式就是在服務端和你的app中開啟gzip。這對於文字這種能有更高壓縮率的資料來說會有更顯著的效用。

當然,現在蘋果已經自動支援了,你只需要告訴你們服務端的同學,傳輸大檔案的時候記得用gzip就完了。

2.6 View的重用和懶載入

更多的view意味著更多的渲染,也就是更多的CPU和記憶體消耗,對於那種嵌套了很多view在UIScrollView裡邊的app更是如此。

重用就是模仿UITableViewUICollectionView的操作: 不要一次建立所有的subview,而是當需要時才建立,當它們完成了使命,把他們放進一個可重用的隊列中。
當需要使用View的時候,去可重用隊列裡面找一找有沒有可以被複用的View。
這裡我的一份架構中曾經使用過類似的方法去建立一個圖片瀏覽器,大家可以稍做參考。View的重用

懶載入就是在程式啟動時並不進行載入,只有當用到這個對象的時候,才進行載入。
這個不僅在屬性中可以進行這樣的使用,在View上面也是一樣,不過實現稍有不同。
懶載入會消耗更少記憶體,但是在View的顯示上會稍有滯後。

2.7 Cache

一個極好的原則就是,緩衝所需要的,也就是那些不大可能改變但是需要經常讀取的東西。

我們能緩衝些什麼呢?一些選項是,遠端伺服器的響應,圖片,甚至計算結果,比如UITableView的行高。

NSURLConnection預設會緩衝資源在記憶體或者儲存中根據它所載入的HTTP Headers。你甚至可以手動建立一個NSURLRequest然後使它只載入緩衝的值。

下面是一個可用的程式碼片段,你可以可以用它去為一個基本不會改變的圖片建立一個NSURLRequest並緩衝它:

+ (NSMutableURLRequest *)imageRequestWithURL:(NSURL *)url {    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];     request.cachePolicy = NSURLRequestReturnCacheDataElseLoad; // this will make sure the request always returns the cached image    request.HTTPShouldHandleCookies = NO;    request.HTTPShouldUsePipelining = YES;    [request addValue:@"image/*" forHTTPHeaderField:@"Accept"];     return request;}

注意你可以通過 NSURLConnection 擷取一個URL request, AFNetworking也一樣的。這樣你就不必為採用這條tip而改變所有的networking代碼了。

如果你需要緩衝其它不是HTTP Request的東西,你可以用NSCache。

2.8 記得處理記憶體警告

一旦系統記憶體過低,iOS會通知所有運行中app。在官方文檔中是這樣記述:

  如果你的app收到了記憶體警告,它就需要儘可能釋放更多的記憶體。最佳方式是移除對緩衝,圖片object和其他一些可以重建立的objects的strong references.

幸運的是,UIKit提供了幾種收集低記憶體警告的方法:

  在app delegate中使用<code>applicationDidReceiveMemoryWarning:</code> 的方法 在你的自訂UIViewController的子類(subclass)中覆蓋<code>didReceiveMemoryWarning</code> 註冊並接收 UIApplicationDidReceiveMemoryWarningNotification 的通知

一旦收到這類通知,你就需要釋放任何不必要的記憶體使用量。

  例如,UIViewController的預設行為是移除一些不可見的view, 它的一些子類則可以補充這個方法,刪掉一些額外的資料結構。一個有圖片緩衝的app可以移除不在螢幕上顯示的圖片。

這樣對記憶體警報的處理是很必要的,若不重視,你的app就可能被系統殺掉。

然而,當你一定要確認你所選擇的object是可以被重現建立的來釋放記憶體。一定要在開發中用模擬器中的記憶體提醒類比去測試一下。

2.9 重用大的開銷對象

這裡的大開銷是指一些初始化很慢的objects,如:NSDateFormatter和NSCalendar。但是,你又不可避免地需要使用它們,比如從JSON或者XML中解析資料。

想要避免使用這個對象的瓶頸你就需要重用他們,可以通過添加屬性到你的class裡或者建立靜態變數來實現。

注意如果你要選擇第二種方法,對象會在你的app運行時一直存在於記憶體中,和單例(singleton)很相似。

下面的代碼說明了使用一個屬性來消極式載入一個date formatter. 第一次調用時它會建立一個新的執行個體,以後的調用則將返回已經建立的執行個體:

// in your .h or inside a class extension@property (nonatomic, strong) NSDateFormatter *formatter; // inside the implementation (.m)// When you need, just use self.formatter- (NSDateFormatter *)formatter {    if (! _formatter) {        _formatter = [[NSDateFormatter alloc] init];        _formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy"; // twitter date format    }    return _formatter;}

還需要注意的是,其實設定一個NSDateFormatter的速度差不多是和建立新的一樣慢的!所以如果你的app需要經常進行日期格式處理的話,你會從這個方法中得到不小的效能提升。

2.10 避免反覆的處理資料

許多應用需要從伺服器載入功能所需的常為JSON或者XML格式的資料。在伺服器端和用戶端使用相同的資料結構很重要。在記憶體中操作資料使它們滿足你的資料結構是開銷很大的。

比如你需要資料來展示一個table view,最好直接從伺服器取array結構的資料以避免額外的中間資料結構改變。

類似的,如果需要從特定key中取資料,那麼就使用索引值對的dictionary。

2.11 正確設定背景圖片

在View裡放背景圖片就像很多其它iOS編程一樣有很多方法:

  使用UIColor的 colorWithPatternImage來設定背景色; 在view中添加一個UIImageView作為一個子View。

如果你使用全畫幅的背景圖,你就必須使用UIImageView因為UIColor的colorWithPatternImage是用來建立小的重複的圖片作為背景的。這種情形下使用UIImageView可以節約不少的記憶體:

// You could also achieve the same result in Interface BuilderUIImageView *backgroundView = [ [UIImageView alloc] initWithImage:[UIImage imageNamed:@"background"]];[self.view addSubview:backgroundView];

如果你用小圖平鋪來建立背景,你就需要用UIColor的colorWithPatternImage來做了,它會更快地渲染也不會花費很多記憶體:

self.view.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"background"]];
2.12 試試蘋果最新的WKWebView來處理web

UIWebView很有用,用它來展示網頁內容或者建立UIKit很難做到的動畫效果是很簡單的一件事。

但是你可能有注意到UIWebView並不像驅動Safari的那麼快。這是由於以JIT compilation 為特色的Webkit的Nitro Engine的限制。

所以想要更高的效能你就要調整下你的HTML了。第一件要做的事就是儘可能移除不必要的javascript,避免使用過大的架構。能只用原生js就更好了。

另外,儘可能非同步載入例如使用者行為統計script這種不影響頁面表達的javascript。

最後,永遠要注意你使用的圖片,保證圖片的符合你使用的大小。使用Sprite sheet提高載入速度和節約記憶體。

當然,上面是針對你在使用UIWebView的情況下,需要盡量減少使用web的特性,而蘋果最近剛推出的Safari的底層架構WKWebView也許能幫我們規避掉很多這樣的效能問題。

2.13 最佳化你的TableView

Table view需要有很好的滾動效能,不然使用者會在滾動過程中發現動畫的瑕疵。

為了保證table view平滑滾動,確保你採取了以下的措施:

  正確使用reuseIdentifier來重用cells 盡量使所有的view opaque,包括cell自身 避免漸層,圖片縮放,後台選人 緩衝行高 如果cell內現實的內容來自web,使用非同步載入,緩衝請求結果 使用shadowPath來畫陰影 減少subviews的數量 盡量不適用cellForRowAtIndexPath:,如果你需要用到它,只用一次然後緩衝結果 使用正確的資料結構來儲存資料 使用rowHeight, sectionFooterHeight 和 sectionHeaderHeight來設定固定的高,不要請求delegate

2.14 選擇正確的資料存放區方式

當儲存大塊資料時你會怎麼做?

你有很多選擇,比如:

  使用NSUerDefaults 使用XML, JSON, 或者 plist 使用NSCoding存檔 使用類似SQLite的本地SQL資料庫 使用 Core Data

NSUserDefaults的問題是什嗎?雖然它很nice也很便捷,但是它只適用於小資料,比如一些簡單的布爾型的設定選項,再大點你就要考慮其它方式了

XML這種結構化檔案呢?總體來說,你需要讀取整個檔案到記憶體裡去解析,這樣是很不經濟的。使用SAX又是一個很麻煩的事情。

NSCoding?不幸的是,它也需要讀寫檔案,所以也有以上問題。

在這種應用情境下,使用SQLite 或者 Core Data比較好。使用這些技術你用特定的查詢語句就能只載入你需要的對象。

在效能層面來講,SQLite和Core Data是很相似的。他們的不同在於具體使用方法。Core Data代表一個對象的graph model,但SQLite就是一個DBMS。Apple在一般情況下建議使用Core Data,但是如果你有理由不使用它,那麼就去使用更加底層的SQLite吧。

如果你使用SQLite,你可以用FMDB(https://github.com/ccgus/fmdb)這個庫來簡化SQLite的操作,這樣你就不用花很多經曆瞭解SQLite的C API了。

2.15 把Xib換成Storyboard吧

當你載入一個XIB的時候所有內容都被放在了記憶體裡,包括任何圖片。如果有一個不會即刻用到的view,你這就是在浪費寶貴的記憶體資源了。

Storyboards就是另一碼事兒了,storyboard僅在需要時執行個體化一個view controller.

當載入XIB時,所有圖片都被緩衝,如果你在做OS X開發的話,音效檔也是。Apple在相關文檔中的記述是:

  當你載入一個引用了圖片或者聲音資源的nib時,nib載入代碼會把圖片和音效檔寫進記憶體。在OS X中,圖片和聲音資源被緩衝在named cache中以便將來用到時擷取。在iOS中,僅圖片資源會被存進named caches。取決於你所在的平台,使用NSImage 或UIImage 的`imageNamed:`方法來擷取圖片資源。

很明顯,同樣的事情也發生在storyboards中,但我並沒有找到任何支援這個結論的文檔。

另外,快速開啟app是很重要的,特別是使用者第一次開啟它時,對app來講,第一印象太太太重要了。

你能做的就是使它儘可能做更多的非同步任務,比如載入遠端或者資料庫資料,解析資料。

還是那句話,避免過於龐大的XIB,因為他們是在主線程上載入的。所以盡量使用沒有這個問題的Storyboards吧!

  注意,用Xcode debug時watchdog並不運行,一定要把裝置從Xcode斷開來測試啟動速度

2.16 學會手動建立Autorelease Pool

NSAutoreleasePool負責釋放block中的autoreleased objects。一般情況下它會自動被UIKit調用。但是有些狀況下你也需要手動去建立它。

假如你建立很多臨時對象,你會發現記憶體一直在減少直到這些對象被release的時候。這是因為只有當UIKit用光了autorelease pool的時候memory才會被釋放。

好訊息是你可以在你自己的@autoreleasepool裡建立臨時的對象來避免這個行為:

NSArray *urls = [@"url1",@"url2"];for (NSURL *url in urls) {    @autoreleasepool {        NSError *error;        NSString *fileContents = [NSString stringWithContentsOfURL: url encoding: NSUTF8StringEncoding error: &error];        /* Process the string, creating and autoreleasing more objects. */    }}

這段代碼在每次遍曆後釋放所有autorelease對象。

轉載至:連結

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.