成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[183880]]

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

Time Profiler

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

Allocations

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

Leak

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

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

NSDateFormatter

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

UIImage

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

頁面加載

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

優化***加載時間

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

監控卡頓的方法

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

 

  1. int32_t __CFRunLoopRun() 
  2.  
  3.  
  4.     __CFRunLoopDoObservers(KCFRunLoopEntry); 
  5.  
  6.     do 
  7.  
  8.     { 
  9.  
  10.         __CFRunLoopDoObservers(kCFRunLoopBeforeTimers); 
  11.  
  12.         __CFRunLoopDoObservers(kCFRunLoopBeforeSources); //這里開始到kCFRunLoopBeforeWaiting之間處理時間是感知卡頓的關鍵地方 
  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.  

 

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

  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.     //創建一個觀察者 
  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.     //創建子線程監控 
  76.  
  77.     dispatch_async(dispatch_get_global_queue(0, 0), ^{ 
  78.  
  79.         //子線程開啟一個持續的loop用來進行監控 
  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的狀態,BeforeSources和AfterWaiting這兩個狀態區間時間能夠檢測到是否卡頓 
  100.  
  101.                 if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) { 
  102.  
  103.                     //出現三次出結果 
  104.  
  105.                     if (++timeoutCount 3) { 
  106.  
  107.                         continue
  108.  
  109.                     } 
  110.  
  111.   
  112.  
  113.                     //將堆棧信息上報服務器的代碼放到這里 
  114.  
  115.   
  116.  
  117.                 } //end activity 
  118.  
  119.             }// end semaphore wait 
  120.  
  121.             timeoutCount = 0; 
  122.  
  123.         }// end while 
  124.  
  125.     }); 
  126.  
  127.   
  128.  

 

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

堆棧dump的方法

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

  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]; //得到當前調用棧信息 
  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.     //系統錯誤信號捕獲 
  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. //將字符串上傳服務器 
  10.  
  11. NSLog(@"lag happen, detail below: 
  12.  
  13. %@",lagReportString); 

 

測試Demo里堆棧中的內容,超過了微信正文字數,所以本文省略了 

責任編輯:龐桂玉 來源: iOS大全
相關推薦

2019-09-17 09:21:01

2018-06-14 09:35:35

2012-08-22 13:00:08

2009-06-18 13:42:48

Hibernate s

2011-03-11 09:27:11

Java性能監控

2021-06-10 10:02:19

優化緩存性能

2015-03-16 10:00:40

iOSARCMRC

2015-07-28 14:39:02

IOS技巧

2018-02-06 11:10:27

iOS開發Xcode快捷鍵

2013-08-27 13:24:46

App Store應用上傳應用截圖ASO應用商店優化

2012-06-15 09:41:40

Linux內核

2011-08-31 10:54:25

Java性能

2021-04-19 17:25:08

Kubernetes組件網絡

2023-09-04 16:55:18

2017-05-10 14:49:52

Kotlin語言Java

2015-07-28 14:52:35

IOS技巧

2014-05-13 09:55:13

iOS開發工具

2012-12-24 14:51:02

iOS

2018-02-04 22:29:21

iOS開發

2009-08-27 10:06:15

Scala的構造方法
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲毛片在线 | 黑人巨大精品欧美一区二区免费 | 亚洲黄色av | 欧美精品1区2区3区 精品国产欧美一区二区 | 成人亚洲视频 | 蜜桃av一区二区三区 | 一区二区三区免费在线观看 | 久久久久国产一级毛片 | 日日夜精品视频 | 亚洲不卡在线观看 | 天天夜碰日日摸日日澡 | 国产一级免费视频 | www精品美女久久久tv | 久久久精品综合 | 四色永久 | 亚洲九九精品 | 日本精品一区二区三区在线观看 | 黄色一级毛片免费看 | 二区在线观看 | 成人毛片视频免费 | 成人国产在线视频 | www.99热这里只有精品 | 久久国内 | 日本精品一区二区三区视频 | 日韩色在线 | 你懂的在线视频播放 | 日本中文在线视频 | 超碰日韩 | 国产专区在线 | av片免费 | 伊人免费在线观看 | 亚洲精品99 | 亚洲国产高清高潮精品美女 | 欧美天堂一区 | 精品国产一区二区三区免费 | 51ⅴ精品国产91久久久久久 | 在线免费看黄 | 国产高清一区二区三区 | 最新日韩在线 | 国产精品美女视频 | 日韩视频一区在线观看 |