自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

檢測iOS的APP 性能的一些方法

移動開發(fā) iOS
首先如果遇到應(yīng)用卡頓或者因為內(nèi)存占用過多時一般使用Instruments里的來進(jìn)行檢測。但對于復(fù)雜情況可能就需要用到子線程監(jiān)控主線程的方式來了,下面我對這些方法做些介紹。

[[183880]]

首先如果遇到應(yīng)用卡頓或者因為內(nèi)存占用過多時一般使用Instruments里的來進(jìn)行檢測。但對于復(fù)雜情況可能就需要用到子線程監(jiān)控主線程的方式來了,下面我對這些方法做些介紹:

Time Profiler

可以查看多個線程里那些方法費(fèi)時過多的方法。先將右側(cè)Hide System Libraries打上勾,這樣能夠過濾信息。然后在Call Tree上會默認(rèn)按照費(fèi)時的線程進(jìn)行排序,單個線程中會也會按照對應(yīng)的費(fèi)時方法排序,選擇方法后能夠通過右側(cè)Heaviest Stack Trace里雙擊查看到具體的費(fèi)時操作代碼,從而能夠有針對性的優(yōu)化,而不需要在一些本來就不會怎么影響性能的地方過度優(yōu)化。

Allocations

這里可以對每個動作的前后進(jìn)行Generations,對比內(nèi)存的增加,查看使內(nèi)存增加的具體的方法和代碼所在位置。具體操作是在右側(cè)Generation Analysis里點擊Mark Generation,這樣會產(chǎn)生一個Generation,切換到其他頁面或一段時間產(chǎn)生了另外一個事件時再點Mark Generation來產(chǎn)生一個新的Generation,這樣反復(fù),生成多個Generation,查看這幾個Generation會看到Growth的大小,如果太大可以點進(jìn)去查看相應(yīng)占用較大的線程里右側(cè)Heaviest Stack Trace里查看對應(yīng)的代碼塊,然后進(jìn)行相應(yīng)的處理。

Leak

可以在上面區(qū)域的Leaks部分看到對應(yīng)的時間點產(chǎn)生的溢出,選擇后在下面區(qū)域的Statistics>Allocation Summary能夠看到泄漏的對象,同樣可以通過Stack Trace查看到具體對應(yīng)的代碼區(qū)域。

開發(fā)時需要注意如何避免一些性能問題

NSDateFormatter

通過Instruments的檢測會發(fā)現(xiàn)創(chuàng)建NSDateFormatter或者設(shè)置NSDateFormatter的屬性的耗時總是排在前面,如何處理這個問題呢,比較推薦的是添加屬性或者創(chuàng)建靜態(tài)變量,這樣能夠使得創(chuàng)建初始化這個次數(shù)降到***。還有就是可以直接用C,或者這個NSData的Category來解決https://github.com/samsoffes/sstoolkit/blob/master/SSToolkit/NSData%2BSSToolkitAdditions.m

UIImage

這里要主要是會影響內(nèi)存的開銷,需要權(quán)衡下imagedNamed和imageWithContentsOfFile,了解兩者特性后,在只需要顯示一次的圖片用后者,這樣會減少內(nèi)存的消耗,但是頁面顯示會增加Image IO的消耗,這個需要注意下。由于imageWithContentsOfFile不緩存,所以需要在每次頁面顯示前加載一次,這個IO的操作也是需要考慮權(quán)衡的一個點。

頁面加載

如果一個頁面內(nèi)容過多,view過多,這樣將長頁面中的需要滾動才能看到的那個部分視圖內(nèi)容通過開啟新的線程同步的加載。

優(yōu)化***加載時間

通過Time Profier可以查看到啟動所占用的時間,如果太長可以通過Heaviest Stack Trace找到費(fèi)時的方法進(jìn)行改造。

監(jiān)控卡頓的方法

還有種方法是在程序里去監(jiān)控性能問題。可以先看看這個Demo,地址https://github.com/ming1016/DecoupleDemo。 這樣在上線后可以通過這個程序?qū)⒂脩舻目D操作記錄下來,定時發(fā)到自己的服務(wù)器上,這樣能夠更大范圍的收集性能問題。眾所周知,用戶層面感知的卡頓都是來自處理所有UI的主線程上,包括在主線程上進(jìn)行的大計算,大量的IO操作,或者比較重的繪制工作。如何監(jiān)控主線程呢,首先需要知道的是主線程和其它線程一樣都是靠NSRunLoop來驅(qū)動的??梢韵瓤纯碈FRunLoopRun的大概的邏輯

 

  1. int32_t __CFRunLoopRun() 
  2.  
  3.  
  4.     __CFRunLoopDoObservers(KCFRunLoopEntry); 
  5.  
  6.     do 
  7.  
  8.     { 
  9.  
  10.         __CFRunLoopDoObservers(kCFRunLoopBeforeTimers); 
  11.  
  12.         __CFRunLoopDoObservers(kCFRunLoopBeforeSources); //這里開始到kCFRunLoopBeforeWaiting之間處理時間是感知卡頓的關(guān)鍵地方 
  13.  
  14.   
  15.  
  16.         __CFRunLoopDoBlocks(); 
  17.  
  18.         __CFRunLoopDoSource0(); //處理UI事件 
  19.  
  20.   
  21.  
  22.         //GCD dispatch main queue 
  23.  
  24.         CheckIfExistMessagesInMainDispatchQueue(); 
  25.  
  26.   
  27.  
  28.         //休眠前 
  29.  
  30.         __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting); 
  31.  
  32.   
  33.  
  34.         //等待msg 
  35.  
  36.         mach_port_t wakeUpPort = SleepAndWaitForWakingUpPorts(); 
  37.  
  38.   
  39.  
  40.         //等待中 
  41.  
  42.   
  43.  
  44.         //休眠后,喚醒 
  45.  
  46.         __CFRunLoopDoObservers(kCFRunLoopAfterWaiting); 
  47.  
  48.   
  49.  
  50.         //定時器喚醒 
  51.  
  52.         if (wakeUpPort == timerPort) 
  53.  
  54.             __CFRunLoopDoTimers(); 
  55.  
  56.   
  57.  
  58.         //異步處理 
  59.  
  60.         else if (wakeUpPort == mainDispatchQueuePort) 
  61.  
  62.             __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() 
  63.  
  64.   
  65.  
  66.         //UI,動畫 
  67.  
  68.         else 
  69.  
  70.             __CFRunLoopDoSource1(); 
  71.  
  72.   
  73.  
  74.         //確保同步 
  75.  
  76.         __CFRunLoopDoBlocks(); 
  77.  
  78.   
  79.  
  80.     } while (!stop && !timeout); 
  81.  
  82.   
  83.  
  84.     //退出RunLoop 
  85.  
  86.     __CFRunLoopDoObservers(CFRunLoopExit); 
  87.  

 

根據(jù)這個RunLoop我們能夠通過CFRunLoopObserverRef來度量。用GCD里的dispatch_semaphore_t開啟一個新線程,設(shè)置一個極限值和出現(xiàn)次數(shù)的值,然后獲取主線程上在kCFRunLoopBeforeSources到kCFRunLoopBeforeWaiting再到kCFRunLoopAfterWaiting兩個狀態(tài)之間的超過了極限值和出現(xiàn)次數(shù)的場景,將堆棧dump下來,***發(fā)到服務(wù)器做收集,通過堆棧能夠找到對應(yīng)出問題的那個方法。

  1. static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) 
  2.  
  3.  
  4.     MyClass *object = (__bridge MyClass*)info; 
  5.  
  6.     object->activity = activity; 
  7.  
  8.  
  9.   
  10.  
  11. static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ 
  12.  
  13.     SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info; 
  14.  
  15.     lagMonitor->runLoopActivity = activity; 
  16.  
  17.   
  18.  
  19.     dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore; 
  20.  
  21.     dispatch_semaphore_signal(semaphore); 
  22.  
  23.  
  24.   
  25.  
  26. - (void)endMonitor { 
  27.  
  28.     if (!runLoopObserver) { 
  29.  
  30.         return
  31.  
  32.     } 
  33.  
  34.     CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes); 
  35.  
  36.     CFRelease(runLoopObserver); 
  37.  
  38.     runLoopObserver = NULL
  39.  
  40.  
  41.   
  42.  
  43. - (void)beginMonitor { 
  44.  
  45.     if (runLoopObserver) { 
  46.  
  47.         return
  48.  
  49.     } 
  50.  
  51.     dispatchSemaphore = dispatch_semaphore_create(0); //Dispatch Semaphore保證同步 
  52.  
  53.     //創(chuàng)建一個觀察者 
  54.  
  55.     CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL}; 
  56.  
  57.     runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, 
  58.  
  59.                                               kCFRunLoopAllActivities, 
  60.  
  61.                                               YES, 
  62.  
  63.                                               0, 
  64.  
  65.                                               &runLoopObserverCallBack, 
  66.  
  67.                                               &context); 
  68.  
  69.     //將觀察者添加到主線程runloop的common模式下的觀察中 
  70.  
  71.     CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes); 
  72.  
  73.   
  74.  
  75.     //創(chuàng)建子線程監(jiān)控 
  76.  
  77.     dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
  78.  
  79.         //子線程開啟一個持續(xù)的loop用來進(jìn)行監(jiān)控 
  80.  
  81.         while (YES) { 
  82.  
  83.             long semaphoreWait = dispatch_semaphore_wait(dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 30*NSEC_PER_MSEC)); 
  84.  
  85.             if (semaphoreWait != 0) { 
  86.  
  87.                 if (!runLoopObserver) { 
  88.  
  89.                     timeoutCount = 0; 
  90.  
  91.                     dispatchSemaphore = 0; 
  92.  
  93.                     runLoopActivity = 0; 
  94.  
  95.                     return
  96.  
  97.                 } 
  98.  
  99.                 //兩個runloop的狀態(tài),BeforeSources和AfterWaiting這兩個狀態(tài)區(qū)間時間能夠檢測到是否卡頓 
  100.  
  101.                 if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) { 
  102.  
  103.                     //出現(xiàn)三次出結(jié)果 
  104.  
  105.                     if (++timeoutCount 3) { 
  106.  
  107.                         continue
  108.  
  109.                     } 
  110.  
  111.   
  112.  
  113.                     //將堆棧信息上報服務(wù)器的代碼放到這里 
  114.  
  115.   
  116.  
  117.                 } //end activity 
  118.  
  119.             }// end semaphore wait 
  120.  
  121.             timeoutCount = 0; 
  122.  
  123.         }// end while 
  124.  
  125.     }); 
  126.  
  127.   
  128.  

 

有時候造成卡頓是因為數(shù)據(jù)異常,過多,或者過大造成的,亦或者是操作的異常出現(xiàn)的,這樣的情況可能在平時日常開發(fā)測試中難以遇到,但是在真實的特別是用戶受眾廣的情況下會有人出現(xiàn),這樣這種收集卡頓的方式還是有價值的。

堆棧dump的方法

***種是直接調(diào)用系統(tǒng)函數(shù)獲取棧信息,這種方法只能夠獲得簡單的信息,沒法配合dSYM獲得具體哪行代碼出了問題,類型也有限。這種方法的主要思路是signal進(jìn)行錯誤信號的獲取。代碼如下

  1. static int s_fatal_signals[] = { 
  2.  
  3.     SIGABRT, 
  4.  
  5.     SIGBUS, 
  6.  
  7.     SIGFPE, 
  8.  
  9.     SIGILL, 
  10.  
  11.     SIGSEGV, 
  12.  
  13.     SIGTRAP, 
  14.  
  15.     SIGTERM, 
  16.  
  17.     SIGKILL, 
  18.  
  19. }; 
  20.  
  21.   
  22.  
  23. static int s_fatal_signal_num = sizeof(s_fatal_signals) / sizeof(s_fatal_signals[0]); 
  24.  
  25.   
  26.  
  27. void UncaughtExceptionHandler(NSException *exception) { 
  28.  
  29.     NSArray *exceptionArray = [exception callStackSymbols]; //得到當(dāng)前調(diào)用棧信息 
  30.  
  31.     NSString *exceptionReason = [exception reason];       //非常重要,就是崩潰的原因 
  32.  
  33.     NSString *exceptionName = [exception name];           //異常類型 
  34.  
  35.  
  36.   
  37.  
  38. void SignalHandler(int code) 
  39.  
  40.  
  41.     NSLog(@"signal handler = %d",code); 
  42.  
  43.  
  44.   
  45.  
  46. void InitCrashReport() 
  47.  
  48.  
  49.     //系統(tǒng)錯誤信號捕獲 
  50.  
  51.     for (int i = 0; i signal(s_fatal_signals[i], SignalHandler); 
  52.  
  53.     } 
  54.  
  55.   
  56.  
  57.     //oc未捕獲異常的捕獲 
  58.  
  59.     NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); 
  60.  
  61.  
  62. int main(int argc, char * argv[]) { 
  63.  
  64.     @autoreleasepool { 
  65.  
  66.         InitCrashReport(); 
  67.  
  68.         return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
  69.  
  70.     } 
  71.  

 

使用PLCrashReporter的話出的報告看起來能夠定位到問題代碼的具體位置了。

  1. NSData *lagData = [[[PLCrashReporter alloc] 
  2.  
  3.                                           initWithConfiguration:[[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll]] generateLiveReport]; 
  4.  
  5. PLCrashReport *lagReport = [[PLCrashReport alloc] initWithData:lagData error:NULL]; 
  6.  
  7. NSString *lagReportString = [PLCrashReportTextFormatter stringValueForCrashReport:lagReport withTextFormat:PLCrashReportTextFormatiOS]; 
  8.  
  9. //將字符串上傳服務(wù)器 
  10.  
  11. NSLog(@"lag happen, detail below: 
  12.  
  13. %@",lagReportString); 

 

測試Demo里堆棧中的內(nèi)容,超過了微信正文字?jǐn)?shù),所以本文省略了 

責(zé)任編輯:龐桂玉 來源: iOS大全
相關(guān)推薦

2019-09-17 09:21:01

2018-06-14 09:35:35

2012-08-22 13:00:08

2015-03-16 10:00:40

iOSARCMRC

2009-06-18 13:42:48

Hibernate s

2011-03-11 09:27:11

Java性能監(jiān)控

2021-06-10 10:02:19

優(yōu)化緩存性能

2015-07-28 14:39:02

IOS技巧

2018-02-06 11:10:27

iOS開發(fā)Xcode快捷鍵

2013-08-27 13:24:46

App Store應(yīng)用上傳應(yīng)用截圖ASO應(yīng)用商店優(yōu)化

2011-08-31 10:54:25

Java性能

2023-09-04 16:55:18

2017-05-10 14:49:52

Kotlin語言Java

2012-06-15 09:41:40

Linux內(nèi)核

2021-04-19 17:25:08

Kubernetes組件網(wǎng)絡(luò)

2012-12-24 14:51:02

iOS

2018-02-04 22:29:21

iOS開發(fā)

2014-05-13 09:55:13

iOS開發(fā)工具

2015-07-28 14:52:35

IOS技巧

2009-08-27 10:06:15

Scala的構(gòu)造方法
點贊
收藏

51CTO技術(shù)棧公眾號