日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

检测iOS的APP性能的一些方法

發布時間:2024/8/26 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 检测iOS的APP性能的一些方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先如果遇到應用卡頓或者因為內存占用過多時一般使用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的大概的邏輯

int32_t __CFRunLoopRun() {__CFRunLoopDoObservers(KCFRunLoopEntry);do{__CFRunLoopDoObservers(kCFRunLoopBeforeTimers); __CFRunLoopDoObservers(kCFRunLoopBeforeSources); //這里開始到kCFRunLoopBeforeWaiting之間處理時間是感知卡頓的關鍵地方 __CFRunLoopDoBlocks(); __CFRunLoopDoSource0(); //處理UI事件 //GCD dispatch main queue CheckIfExistMessagesInMainDispatchQueue(); //休眠前 __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting); //等待msg mach_port_t wakeUpPort = SleepAndWaitForWakingUpPorts(); //等待中 //休眠后,喚醒 __CFRunLoopDoObservers(kCFRunLoopAfterWaiting); //定時器喚醒 if (wakeUpPort == timerPort) __CFRunLoopDoTimers(); //異步處理 else if (wakeUpPort == mainDispatchQueuePort) __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() //UI,動畫 else __CFRunLoopDoSource1(); //確保同步 __CFRunLoopDoBlocks(); } while (!stop && !timeout); //退出RunLoop __CFRunLoopDoObservers(CFRunLoopExit); }

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

static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { MyClass *object = (__bridge MyClass*)info; object->activity = activity; } static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){ SMLagMonitor *lagMonitor = (__bridge SMLagMonitor*)info; lagMonitor->runLoopActivity = activity; dispatch_semaphore_t semaphore = lagMonitor->dispatchSemaphore; dispatch_semaphore_signal(semaphore); } - (void)endMonitor { if (!runLoopObserver) { return; } CFRunLoopRemoveObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes); CFRelease(runLoopObserver); runLoopObserver = NULL; } - (void)beginMonitor { if (runLoopObserver) { return; } dispatchSemaphore = dispatch_semaphore_create(0); //Dispatch Semaphore保證同步 //創建一個觀察者 CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL}; runLoopObserver = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context); //將觀察者添加到主線程runloop的common模式下的觀察中 CFRunLoopAddObserver(CFRunLoopGetMain(), runLoopObserver, kCFRunLoopCommonModes); //創建子線程監控 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //子線程開啟一個持續的loop用來進行監控 while (YES) { long semaphoreWait = dispatch_semaphore_wait(dispatchSemaphore, dispatch_time(DISPATCH_TIME_NOW, 30*NSEC_PER_MSEC)); if (semaphoreWait != 0) { if (!runLoopObserver) { timeoutCount = 0; dispatchSemaphore = 0; runLoopActivity = 0; return; } //兩個runloop的狀態,BeforeSources和AfterWaiting這兩個狀態區間時間能夠檢測到是否卡頓 if (runLoopActivity == kCFRunLoopBeforeSources || runLoopActivity == kCFRunLoopAfterWaiting) { //出現三次出結果 if (++timeoutCount < 3) { continue; } //將堆棧信息上報服務器的代碼放到這里 } //end activity }// end semaphore wait timeoutCount = 0; }// end while }); }

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

堆棧dump的方法

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

static int s_fatal_signals[] = {SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGSEGV,SIGTRAP,SIGTERM,SIGKILL, };static int s_fatal_signal_num = sizeof(s_fatal_signals) / sizeof(s_fatal_signals[0]); void UncaughtExceptionHandler(NSException *exception) { NSArray *exceptionArray = [exception callStackSymbols]; //得到當前調用棧信息 NSString *exceptionReason = [exception reason]; //非常重要,就是崩潰的原因 NSString *exceptionName = [exception name]; //異常類型 } void SignalHandler(int code) { NSLog(@"signal handler = %d",code); } void InitCrashReport() { //系統錯誤信號捕獲 for (int i = 0; i < s_fatal_signal_num; ++i) { signal(s_fatal_signals[i], SignalHandler); } //oc未捕獲異常的捕獲 NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler); } int main(int argc, char * argv[]) { @autoreleasepool { InitCrashReport(); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } }

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

NSData *lagData = [[[PLCrashReporter alloc]initWithConfiguration:[[PLCrashReporterConfig alloc] initWithSignalHandlerType:PLCrashReporterSignalHandlerTypeBSD symbolicationStrategy:PLCrashReporterSymbolicationStrategyAll]] generateLiveReport]; PLCrashReport *lagReport = [[PLCrashReport alloc] initWithData:lagData error:NULL]; NSString *lagReportString = [PLCrashReportTextFormatter stringValueForCrashReport:lagReport withTextFormat:PLCrashReportTextFormatiOS]; //將字符串上傳服務器 NSLog(@"lag happen, detail below: %@",lagReportString);

下面是測試Demo里堆棧里的內容

2016-03-28 14:59:26.922 HomePageTest[4803:201212] INFO: Reveal Server started (Protocol Version 25). 2016-03-28 14:59:27.134 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:29.262 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:30.865 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:32.115 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:33.369 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:34.832 HomePageTest[4803:201212] 費時測試 2016-03-28 14:59:34.836 HomePageTest[4803:201615] lag happen, detail below: Incident Identifier: 73BEF9D2-EBE3-49DF-B95B-7392635631A3 CrashReporter Key: TODO Hardware Model: x86_64 Process: HomePageTest [4803] Path: /Users/daiming/Library/Developer/CoreSimulator/Devices/444AAB95-C393-45CC-B5DC-0FB8611068F9/data/Containers/Bundle/Application/9CEE3A3A-9469-44F5-8112-FF0550ED8009/HomePageTest.app/HomePageTest Identifier: com.xiaojukeji.HomePageTest Version: 1.0 (1) Code Type: X86-64 Parent Process: debugserver [4805]Date/Time: 2016-03-28 06:59:34 +0000 OS Version: Mac OS X 9.2 (15D21) Report Version: 104Exception Type: SIGTRAP Exception Codes: TRAP_TRACE at 0x10aee6f79 Crashed Thread: 2Thread 0: 0 libsystem_kernel.dylib 0x000000010ec6b206 __semwait_signal + 10 1 libsystem_c.dylib 0x000000010e9f2b9e usleep + 54 2 HomePageTest 0x000000010aedf934 -[TestTableView configCell] + 820 3 HomePageTest 0x000000010aee5c89 -[testTableViewController observeValueForKeyPath:ofObject:change:context:] + 601 4 Foundation 0x000000010b9c2564 NSKeyValueNotifyObserver + 347 5 Foundation 0x000000010b9c178f NSKeyValueDidChange + 466 6 Foundation 0x000000010b9bf003 -[NSObject(NSKeyValueObservingPrivate) _changeValueForKey:key:key:usingBlock:] + 1176 7 Foundation 0x000000010ba1d35f _NSSetObjectValueAndNotify + 261 8 HomePageTest 0x000000010aec9c26 -[DCTableView tableView:cellForRowAtIndexPath:] + 262 9 UIKit 0x000000010c872e43 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 766 10 UIKit 0x000000010c872f7b -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 74 11 UIKit 0x000000010c847a39 -[UITableView _updateVisibleCellsNow:isRecursive:] + 2996 12 UIKit 0x000000010c87c01c -[UITableView _performWithCachedTraitCollection:] + 92 13 UIKit 0x000000010c862edc -[UITableView layoutSubviews] + 224 14 UIKit 0x000000010c7d04a3 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 703 15 QuartzCore 0x000000010c49a59a -[CALayer layoutSublayers] + 146 16 QuartzCore 0x000000010c48ee70 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366 17 QuartzCore 0x000000010c48ecee _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24 18 QuartzCore 0x000000010c483475 _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 277 19 QuartzCore 0x000000010c4b0c0a _ZN2CA11Transaction6commitEv + 486 20 QuartzCore 0x000000010c4bf9f4 _ZN2CA7Display11DisplayLink14dispatch_itemsEyyy + 576 21 CoreFoundation 0x000000010e123c84 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20 22 CoreFoundation 0x000000010e123831 __CFRunLoopDoTimer + 1089 23 CoreFoundation 0x000000010e0e5241 __CFRunLoopRun + 1937 24 CoreFoundation 0x000000010e0e4828 CFRunLoopRunSpecific + 488 25 GraphicsServices 0x0000000110479ad2 GSEventRunModal + 161 26 UIKit 0x000000010c719610 UIApplicationMain + 171 27 HomePageTest 0x000000010aee0fdf main + 111 28 libdyld.dylib 0x000000010e92b92d start + 1Thread 1: 0 libsystem_kernel.dylib 0x000000010ec6bfde kevent64 + 10 1 libdispatch.dylib 0x000000010e8e6262 _dispatch_mgr_init + 0Thread 2 Crashed: 0 HomePageTest 0x000000010b04a445 -[PLCrashReporter generateLiveReportWithThread:error:] + 632 1 HomePageTest 0x000000010aee6f79 __28-[SMLagMonitor beginMonitor]_block_invoke + 425 2 libdispatch.dylib 0x000000010e8d6e5d _dispatch_call_block_and_release + 12 3 libdispatch.dylib 0x000000010e8f749b _dispatch_client_callout + 8 4 libdispatch.dylib 0x000000010e8dfbef _dispatch_root_queue_drain + 1829 5 libdispatch.dylib 0x000000010e8df4c5 _dispatch_worker_thread3 + 111 6 libsystem_pthread.dylib 0x000000010ec2f68f _pthread_wqthread + 1129 7 libsystem_pthread.dylib 0x000000010ec2d365 start_wqthread + 13Thread 3: 0 libsystem_kernel.dylib 0x000000010ec6b6de __workq_kernreturn + 10 1 libsystem_pthread.dylib 0x000000010ec2d365 start_wqthread + 13Thread 4: 0 libsystem_kernel.dylib 0x000000010ec65386 mach_msg_trap + 10 1 CoreFoundation 0x000000010e0e5b64 __CFRunLoopServiceMachPort + 212 2 CoreFoundation 0x000000010e0e4fbf __CFRunLoopRun + 1295 3 CoreFoundation 0x000000010e0e4828 CFRunLoopRunSpecific + 488 4 WebCore 0x0000000113408f65 _ZL12RunWebThreadPv + 469 5 libsystem_pthread.dylib 0x000000010ec2fc13 _pthread_body + 131 6 libsystem_pthread.dylib 0x000000010ec2fb90 _pthread_body + 0 7 libsystem_pthread.dylib 0x000000010ec2d375 thread_start + 13Thread 5: 0 libsystem_kernel.dylib 0x000000010ec6b6de __workq_kernreturn + 10 1 libsystem_pthread.dylib 0x000000010ec2d365 start_wqthread + 13Thread 6: 0 libsystem_kernel.dylib 0x000000010ec6b6de __workq_kernreturn + 10 1 libsystem_pthread.dylib 0x000000010ec2d365 start_wqthread + 13Thread 7: 0 libsystem_kernel.dylib 0x000000010ec6b6de __workq_kernreturn + 10 1 libsystem_pthread.dylib 0x000000010ec2d365 start_wqthread + 13Thread 2 crashed with X86-64 Thread State:rip: 0x000000010b04a445 rbp: 0x0000700000093da0 rsp: 0x0000700000093b10 rax: 0x0000700000093b70 rbx: 0x0000700000093cb0 rcx: 0x0000000000001003 rdx: 0x0000000000000000 rdi: 0x000000010b04a5ca rsi: 0x0000700000093b40 r8: 0x0000000000000014 r9: 0x0000000000000000 r10: 0x000000010ec65362 r11: 0x0000000000000246 r12: 0x00007fdaf5800940 r13: 0x0000000000000000 r14: 0x0000000000000009 r15: 0x0000700000093b90 rflags: 0x0000000000000202 cs: 0x000000000000002b fs: 0x0000000000000000 gs: 0x0000000000000000

?

?

轉載于:https://www.cnblogs.com/fengmin/p/5390040.html

總結

以上是生活随笔為你收集整理的检测iOS的APP性能的一些方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。