[xcode]instruments來檢驗你的app

 2023-10-05 阅读 25 评论 0

摘要:原文網址:http://www.cocoachina.com/industry/20140114/7696.html 比較了好多關于instruments 還是發現老外寫的比較牛逼.于是果斷翻譯過來.有能力的的可以去看英文原版,鼓勵大家看原版資料遠離二手教程。這里是原文:http://www.raywenderlich.com/23037/how-to-use-instrum
原文網址:http://www.cocoachina.com/industry/20140114/7696.html
比較了好多關于instruments 還是發現老外寫的比較牛逼.于是果斷翻譯過來.有能力的的可以去看英文原版,鼓勵大家看原版資料遠離二手教程。這里是原文:http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
入門
為了節省大家的時間,提供一個演示的Demo給大家。代碼傳送門.(請去原文下載)
下載后解壓然后用xcode打開。
編譯運行APP后 然后在搜索框內輸入任意詞匯,點擊結果你會看到下面的結果
正如你所見的,這個app很簡單.程序其實調用的是Flickr的API,通過app頂部的搜索框執行搜索后在下面的tableview顯示你搜索的搜索詞,搜索詞后面的括號內有搜索結果的個數,點擊此行進入一個略所圖的結果列表頁面 如上圖. 點擊其中一行 進入圖像的大圖模式,在這個頁面你可以根據需要旋轉圖像。
到目前為止頁面看起來差不多了,你也許會想應該可以直接提交appstore了吧.接下來這篇文章將會教你instruments工具來提高你app性能和穩定性.
“時間探測器”
天下武功,唯快不破。很多公司都信奉這個教條.恨不得把app壓法周期壓縮到最低,這就導致了開發中隱藏了很多問題,有點經驗的工程師草率的優化下,更糟的情況那些沒有經驗的工程師甚至不會對app進行任何優化.
某種程度上來說,你開發過程中是可以忽略性能優化的. 十年前,移動設備的硬件資源是非常有限的.甚至連浮點數都是被禁止的.因為浮點數能導致代碼變大計算的速度變慢.
科技發展如此迅速的今天,硬件很大程度上可以彌補軟件的短板.現在的移動設備3D硬件處理的效率甚至媲美于PC機了,但是你不能總依賴于硬件和處理器速度來掩飾你APP做的多垃圾吧.(如果安卓系統跑在Iphone上還能夠像iOS一樣順滑嗎?其實是一個道理的)
性能這個概念很抽線,所以我們必須借助數據化圖形化的輸出方式.你可能花一周的時間去優化一個有趣的算法,但是這算法只占總執行時間的0.5%,不管你花多少精力去優化它,沒人會注意到.相反一個for循環花費了90%的時間,你稍微修改下就能提高10%的效率,就是這個簡單的修改可以得到大家很大的好感.因為.他們運行app時的第一感受就是比之前快了很多.沒人會care你修改的是一個多牛逼的算法,還是一個簡單的for循環.
這個說明了什么?
與其花費時間在優化小細節上不如多點時間找到你改優化的地方.
下面引出第一個工具 “時間事件查看器”(自己杜撰的名字英文—Time Profiler),———他可以測量時間的間隔,中斷程序執行,跟蹤每個線程的堆棧.你可以想象下是xcode調試時按下暫停時的畫面
比如,100個樣本都在做1毫秒的間隔,然后在某個方法堆棧頂部有10個樣本,你可以推算出大概的時間有10%個10毫秒花費在此方法中,這是一個近似值.
廢話少說,時間是個檢測到的。
從xcode的菜單選擇Product-Profile,或者選擇?
程序會啟動Instruments,這時候你會看到一個選擇窗口
這是instruments所有測試儀器的面板,選擇 “timer profilter” 點擊“profile”回啟東模擬器和app,此時會要求你輸入一次密碼,以便instruments能有權限去截獲監聽此進程。
在工具窗口中,可以看到時間計數,并留下了一個小箭頭移動到右側的圖形在屏幕的中央上方。這表明該應用程序正在運行。
現在開始運行app,搜索一些圖片,這時候你發現查找一個結果太慢了,而且搜索結果列表頁面滾動起來也是讓人無法忍受的。
首先,確保工具欄中的視圖選擇有選擇的所有三個選項,如下所示:?
這將確保所有的面板都打開。現在,研究下面的截圖和它下面的每個部分的解釋:
1. 錄控按鈕。中間的紅色按鈕將停止與啟動它被點擊時,應用程序目前正在分析。注意這實際上是停止和啟動應用程序,而不是暫停它。 ?
2. 運行定時器和運行導航,定時器顯示APP已經運行了多長時間,箭頭之間是可以移動的。如果停止,然后使用錄制按鈕重新啟動應用程序,這將開始一個新的運行。顯示屏便會顯示“run2 of 2”,你可以回到第一次運行的數據,首先你停止當前運行,然后按下左箭頭回去。 ?
3. 運行軌道。
4. 擴展面板,在時間探查儀器的情況下,它是用來跟蹤顯示堆棧。 ?
5. 詳細地面板。它顯示了你正在使用的儀器的主要信息,這是使用頻率最高的部門,可以從它這里看到cpu運行的時間 ??
6. 選項面板,稍后介紹。
重頭戲來了。
深究
執行圖像搜索,并深究結果。我個人比較喜歡尋找“狗”,當然你也可以選擇任意你想要的內容。比如貓啊美女啊什么的。
現在上下滾動記下列表,讓時間探測器測量下數據,然后注意看下屏幕的變化和數值。這些數值反應了CPU周期。
但是你也許會發現下面的數值太多,看你的眼花繚亂。下面打開左邊的調用樹 然后按著如下的配置
以下介紹下配置選項:
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.花費的時間,這非常真有用,因為它可以讓你每次下到調用堆棧時挑最大的時間數字,歸零在你最耗時的方法。
如果您已啟用上述選項,雖然有些值可能會略有不同,下面的結果的順序應該是類似下表:
通過上面你能看到大部分時間都花在更新表格照片了.
雙擊此行,然后將會看到如下
那么這很有趣,不是嗎!幾乎四分之三的時間花費在setPhoto:方法都花在創造照片的圖像數據!
現在可以看到的是什么問題,NSData’s dataWithContentsOfURL 方法并不會立即返回,因為要從網上去數據,每次調用都需要長達幾秒的時間返回,而此方法運行在主線程,可想而知會有什么結果了.
其實為了解決這個問題,類提供了一個ImageCache 的后臺異步下載的方法.
現在,您可以切換到Xcode和手動找到該文件,但儀器有一個方便的“打開Xcode中”按鈕,就在你的眼前。找到它的面板只是上面的代碼并單擊它:
想如下修改
  1. -?(void)setPhoto:(FlickrPhoto?*)photo?{?
  2. ????_photo?=?photo;?
  3. ??
  4. ????self.textLabel.text?=?photo.title;f?
  5. ??
  6. //????NSData?*imageData?=?[NSData?dataWithContentsOfURL:_photo.thumbnailUrl];??
  7. //????self.imageView.image?=?[UIImage?imageWithData:imageData];?
  8. ??
  9. ????[[ImageCache?sharedInstance]?downloadImageAtURL:_photo.thumbnailUrl?
  10. ??????????????????????????????????completionHandler:^(UIImage?*image)?{?
  11. ??????????????????????????????????????self.imageView.image?=?image;?
  12. ??????????????????????????????????????[self?setNeedsLayout];?
  13. ??????????????????????????????????}];?
  14. }?
修改好厚,在儀器重新運行該應用程序Product—Profile(或cmd-記住,這些快捷鍵真的會為您節省一些時間)。
請注意,這個時候會再問一次你是否使用一起。這是因為你還有一個窗口中打開這個程序,及儀器假定您要使用相同的選項再次運行。
執行一些更多的搜索,并注意此時用戶界面不是那么卡頓了!這些圖像現在異步加載,并緩存在后臺,所以一旦他們已經被下載一次,他們不必再次下載。
看上去很不錯!是時候發布了嗎? 當然還不夠
分配,分配,分配
接下來的儀器是分配工具。它能給出你所有創建和存儲它們的內存的詳細信息,它也顯示你保留了每個對象的計數。
關閉儀器,回到Xcode和選擇Product->Profile。然后,從選擇器分配并單擊配置文件。如下圖:
程序再次打開 然后你會看到
這個時候你會發現兩個曲目。一個叫(分配)Allocations,以及一個被稱為VM Tracker(虛擬機跟蹤)。該分配軌道將詳細在本教程中討論;虛擬機跟蹤也是非常有用的,但更復雜一點。
所以你的錯誤會追蹤下?
有隱藏的項目,你可能不知道有東西在那兒。你可能已經聽說了內存泄漏。但你可能不知道的是,其實有兩種泄漏。
第一個是真正的內存泄漏,一個對象尚未被釋放,但是不再被引用的了。因此,存儲器不能被重新使用。
第二類泄漏是比較麻煩一些。這就是所謂的“無界內存增長”。這發生在內存繼續分配,并永遠不會有機會被釋放。
如果永遠這樣下去你的程序占用的內存會無限大,當超過一定內存的話 會被系統的看門狗給kill掉.
建立一個場景,你可以檢測出無限的內存增長。首先,在應用程序使10個不同的搜索(不要用已經存在的搜索)。確保搜索的一些結果!現在讓程序等待幾秒鐘。
你應該已經注意到,在分配的軌道圖不斷上升。這是告訴你的,內存被分配了。它的這一特征,將引導你找到無限的內存增長。
你將要執行的是“heap shot analysis”。為此,按這個按鈕叫“Mark Heap”。你會發現的詳細面板左側的按鈕
按下它,你會看到一個紅色的標志出現在軌道上,像這樣:
heap shot分析的目的是執行一個動作多次,看看如果內存是否無限增長。搜索一個內容,稍等幾秒加載圖像,然后返回主頁。然后再標記堆。反復這樣做不同的搜索。
演戲幾個搜索后,儀器會看起來像這樣:
這時你應該會疑問。圖中的藍色是怎么回事了,你繼續這樣操作10次這樣的搜索 藍色還不斷變高:
那肯定是不好的。別急,有什么關于內存的警告?你知道這些,對不對?內存警告是告訴一個應用程序,內存警告是ios處理app最好的方式尤其是在內存越來越吃緊的時候,你需要清除一些內存。
內存一直增長其實也不一定是你的代碼除了問題,也有可能是UIKit 系統框架本身導致的.
通過選擇HardwareSimulate內存警告在iOS模擬器的菜單欄模擬內存警告。你會發現,記憶體使用量出現小幅回落,但絕對不會回到它應該的。所以還是有無限的內存增長發生的地方。
究其原因,堆出手做鉆進搜索的每次迭代后,你可以看到內存的分配每個鏡頭之間。一起來看看在詳細信息面板,你會看到一堆一堆的鏡頭。
在iOS模擬器的菜單欄中選擇hardwaresimulate內存警告模擬內存警告。你會發現內存使用會出現小幅回落,但肯定不會回到它應該在的地方。
每一次的搜索后做你可以看到內存已拍攝之間的分配。在詳細信息面板看一看,你會看到一好多堆鏡頭。
穩準狠
第一個堆鏡頭作為參照,然后隨便打開一個堆鏡頭,你會看到如下:
靠,這是一個很大的對象!從哪里開始看呢?
最好的方式是通過列表,你在你的應用程序直接使用的類。在這種情況下,HTTPHeaderDict,CGRegion,CGPath,CFNumber,等等都是可以忽略了。
但是,一個突出的是UIImage,這肯定是在你程序使用的。點擊上的UIImage左側的箭頭顯示的完整列表。選擇一個,在擴展詳細信息面板:
圖中灰色的是系統庫,黑色部分是你應用的代碼,要獲得此跟蹤更多的上下文,雙擊唯一的黑框ImageCache方法,這時候將掉轉到如下方法
  1. -?(void)downloadImageAtURL:(NSURL*)url?completionHandler:(ImageCacheDownloadCompletionHandler)completion?{?
  2. ????UIImage?*cachedImage?=?[self?imageForKey:[url?absoluteString]];?
  3. ????if?(cachedImage)?{?
  4. ????????completion(cachedImage);?
  5. ????}?else?{?
  6. ????????dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,?0),?^{?
  7. ????????????NSData?*data?=?[NSData?dataWithContentsOfURL:url];?
  8. ????????????UIImage?*image?=?[UIImage?imageWithData:data];?
  9. ????????????[self?setImage:image?forKey:[url?absoluteString]];?
  10. ????????????dispatch_async(dispatch_get_main_queue(),?^{?
  11. ????????????????completion(image);?
  12. ????????????});?
  13. ????????});?
  14. ????}?
  15. }?
工具是非常有用的,你現在要努力通過自己的代碼思考發生了什么.看看通過上面的方法,你會看到它調用一個名為setImage方法:forKey:。這種方法在緩存以防它再次使用以后的應用程序的圖像。啊!那么這肯定聽起來像它可能是一個問題!
一起來看看該方法的實現:
  1. -?(void)setImage:(UIImage*)image?forKey:(NSString*)key?{?
  2. ????[_cache?setObject:image?forKey:key];?
  3. }?
從網絡上下載一個圖片添加字典中,你會注意到這些圖片從來沒有從字典清楚過。
,這就是內存為什么會一直增長,因為應用程序并不會從緩存里刪除東西.它只會一直增加他們。
要解決此問題,你需要的是ImageCache收到UIApplication內存吃緊的警告時.清理緩存。
為了使ImageCache能接收通知,修改init方法如下:
  1. -?(id)init?{?
  2. ????if?((self?=?[super?init]))?{?
  3. ????????_cache?=?[NSMutableDictionary?new];?
  4. ????????[[NSNotificationCenter?defaultCenter]?addObserver:self?selector:@selector(memoryWarning:)?name:UIApplicationDidReceiveMemoryWarningNotification?object:nil];?
  5. ????}?
  6. ????return?self;?
  7. }?
注冊UIApplicationDidReceiveMemoryWarningNotification執行memoryWarning:方法。
  1. -?(void)memoryWarning:(NSNotification*)note?{?
  2. ????[_cache?removeAllObjects];?
  3. }?
memoryWarning刪除緩存中的所有對象。這將確保沒有持圖像。
為了測試此修復程序,再次啟動儀器(從Xcode中有cmd)和重復的步驟。不要忘了在模擬結束內存警告!
注意:請確保您從Xcode中退出,重新構建,而不是僅僅點擊儀器儀表上的紅色按鈕,以確保您使用的是最新的代碼。
這一次分析應該是這樣的:
這個時候,內存受到內存警告后急劇下降。但還是有一些內存整體增長,但遠不及像以前那樣。
究其原因還是有一定的增長確實是由于系統庫,并沒有太多可以做的。看來,系統庫不釋放所有的內存,這可能是由設計或可能是一個錯誤。你可以在你的應用程序做的是釋放盡可能多的內存越好,你已經做到這一點!
干得好!還有一個問題,修補了, - 仍然有泄漏,你還沒有解決的第一種類型的問題。
內存泄露
內存泄漏的儀器。這是用來找到第一類泄漏前面提到的 - 當一個對象不再被引用時出現的那種。
檢測泄漏是可以理解的一個很復雜的事情,但泄漏的工具記得,已分配的所有對象,并定期通過掃描每個對象以確定是否有任何不能從任何其他對象訪問的。
關閉儀器,回到Xcode和選擇Product->Profile
回到你的應用程序!執行搜索,得到結果。然后點選結果的預覽行打開全屏瀏覽器。按下旋轉按鈕在左上角,然后再按一次。
回到儀器,等待片刻。如果你已經正確地完成上述步驟后,你會發現泄漏已經出現了!你的工具窗口將看起來像這樣:
返回到模擬器,并按下旋轉幾次。然后返回到儀器和等會,得到如下結果:
哪來的泄漏從哪里來?擴展詳細信息面板
在擴展的詳細信息面板打開CGContext上名單。在列表中選擇CGContext上的元素之一,這表明導致要創建的對象,如下面的堆棧跟蹤:
再次,涉及到你的代碼中的幀顯示為黑色。由于只有一個,雙擊它,看看代碼的方法。
有問題的方法是rotateTapped: ,這是被調用時被竊聽旋轉按鈕的處理程序。這種方法旋轉原始圖像,并創建一個新的圖像,如下:
  1. -?(void)rotateTapped:(id)sender?{?
  2. ????UIImage?*currentImage?=?_imageView.image;?
  3. ????CGImageRef?currentCGImage?=?currentImage.CGImage;?
  4. ??
  5. ????CGSize?originalSize?=?currentImage.size;?
  6. ????CGSize?rotatedSize?=?CGSizeMake(originalSize.height,?originalSize.width);?
  7. ??
  8. ????CGContextRef?context?=?CGBitmapContextCreate(NULL,?
  9. ?????????????????????????????????????????????????rotatedSize.width,?
  10. ?????????????????????????????????????????????????rotatedSize.height,?
  11. ?????????????????????????????????????????????????CGImageGetBitsPerComponent(currentCGImage),?
  12. ?????????????????????????????????????????????????CGImageGetBitsPerPixel(currentCGImage)?*?rotatedSize.width,?
  13. ?????????????????????????????????????????????????CGImageGetColorSpace(currentCGImage),?
  14. ?????????????????????????????????????????????????CGImageGetBitmapInfo(currentCGImage));?
  15. ??
  16. ????CGContextTranslateCTM(context,?rotatedSize.width,?0.0f);?
  17. ????CGContextRotateCTM(context,?M_PI_2);?
  18. ????CGContextDrawImage(context,?(CGRect){.origin=CGPointZero,?.size=originalSize},?currentCGImage);?
  19. ??
  20. ????CGImageRef?newCGImage?=?CGBitmapContextCreateImage(context);?
  21. ????UIImage?*newImage?=?[UIImage?imageWithCGImage:newCGImage];?
  22. ??
  23. ????self.imageView.image?=?newImage;?
  24. }?
再次,儀器只能在這里給你一個提示,問題出在哪里,它不能告訴你確切位置的泄漏。這是唯一能夠證明你在創建對象泄露的地方.你可能認為ARC并有不可能是造成代碼中內存泄漏…對不對?
回想一下,ARC只涉及Objective-C的對象。它不管理保留和的CoreFoundation對象而不是Objective-C的對象的釋放。
啊,現在它開始變得明顯的問題是什么?
-CGContextRef和CGImageRef對象永遠不會被釋放!為了解決這個問題,在rotateTapped方法的末尾添加以下兩行代碼:
  1. CGImageRelease(newCGImage);?
  2. CGContextRelease(context);?
這兩種調用都需要來維護這兩個對象的保留計數。這個說明,你還需要了解引用計數 - 即使你在你的項目中使用的ARC!
從在Xcode中,使用cmd工具構建和運行應用程序。
在使用泄漏儀器儀器再看看應用程序,看看是否泄漏的被固定。如果你正確地遵循上述步驟,泄漏應消失!

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/118365.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息