This article is reproduced in the public number: iOS Daquan
First, if the application is stuck or because the memory is too large, it is usually used in instruments to detect. But for complex situations it may be necessary to use the way the child threads monitor the main thread, I will introduce these methods:
Time Profiler
You can look at ways in which the methods in multiple threads are too time consuming. Tick the right hide System libraries to filter the information. The call tree will then be sorted by default on a time-consuming thread, and a single thread will be sorted by the corresponding time-consuming method, which can be optimized by double-clicking on the heaviest Stack trace in the right-hand side to see the specific time-consuming operation code. There is no need to over-optimize in some places where performance might not have been affected.
Allocations
Here you can generations the front and back of each action, compare the increase in memory, and look at the exact method and location of the code where the memory is added. The specific action is to click Mark Generation in the right Generation analysis, which will produce a Generation, switch to another page, or a period of time to create another event. Generation to generate a new generation, so repeatedly, to generate multiple generation, view these several generation will see the size of growth, if too big can point in to see the corresponding occupied larger thread right heaviest Stack trace to view the corresponding code block, and then do the appropriate processing.
Leak
You can see the overflow in the leaks section of the area above, and after selecting the Statistics>allocation summary in the area below, you can see the leaked objects, as well as using stack trace to see the corresponding code area.
Development needs to be aware of how to avoid some performance issues
NSDateFormatter
Through instruments detection will find the creation of NSDateFormatter or set the properties of NSDateFormatter is always in front of the time, how to deal with this problem, it is recommended to add properties or create static variables, This allows the creation of initialization to be minimized. There is the right to use C, or the NSData category to solve https://github.com/samsoffes/sstoolkit/blob/master/SSToolkit/NSData% 2bsstoolkitadditions.m
UIImage
This is mainly to affect the memory cost, need to weigh the next imagednamed and Imagewithcontentsoffile, understand the characteristics of the two, in the only need to display a picture with the latter, which will reduce the memory consumption, but the page display will increase the consumption of image Io, This needs to be taken care of. Because Imagewithcontentsoffile does not cache, it needs to be loaded once per page display, and the operation of this IO is a point to consider the tradeoff.
Page load
If there is too much content on a page, too many views, so that the part of the view that needs to be scrolled in the long page is loaded by opening a new thread synchronization.
Optimize first load time
Time Profier can be used to see how long it takes to start up, and if it is too lengthy, it can be modified by heaviest Stack trace.
How to monitor the lag
There is also a way to monitor performance issues in the program. Can look at this demo first, address Https://github.com/ming1016/DecoupleDemo. This can be done on-line after the user's lag operation record down, timed to their own server, so that a wider range of collection performance issues. As we all know, user-level perception of the stutter is from the process of processing all the UI on the main thread, including large computations on the main thread, a large number of IO operations, or relatively heavy drawing work. How to monitor the main thread, the first thing to know is that the main thread and other threads are driven by Nsrunloop. You can take a look at Cfrunlooprun's approximate logic.
int32_t __cfrunlooprun () {__cfrunloopdoobservers (kcfrunloopentry); Do{__cfrunloopdoobservers (kcfrunloopbeforetimers); __cfrunloopdoobservers (kcfrunloopbeforesources); //this is where processing time between kcfrunloopbeforewaiting is the key to the perception lag.__cfrunloopdoblocks (); __cfrunloopdosource0 (); //Handling UI Events//GCD Dispatch main queueCheckifexistmessagesinmaindispatchqueue (); //before hibernation__cfrunloopdoobservers (kcfrunloopbeforewaiting); //Wait for msgmach_port_t Wakeupport =Sleepandwaitforwakingupports (); //Waiting in//after sleep, wake up__cfrunloopdoobservers (kcfrunloopafterwaiting); //Timer Wakeup if(Wakeupport = =timerport) __cfrunloopdotimers (); //Asynchronous Processing Else if(Wakeupport = =maindispatchqueueport) __cfrunloop_is_servicing_the_main_dispatch_queue__ ()//UI, Animation Else__cfrunloopdosource1 (); //ensure synchronization__cfrunloopdoblocks (); } while(!stop &&!)timeout); //Exit Runloop__cfrunloopdoobservers (cfrunloopexit);}
According to this runloop we can measure by Cfrunloopobserverref. Use the dispatch_semaphore_t in the GCD to open a new thread, set a limit value and the value of the number of occurrences, Then get the scene that exceeds the limit and number of occurrences on the main thread from kcfrunloopbeforesources to kcfrunloopbeforewaiting to kcfrunloopafterwaiting two states, Dump the stack down, and finally send it to the server to do the collection, through the stack to find the corresponding problem of the method.
Static voidRunloopobservercallback (Cfrunloopobserverref Observer, cfrunloopactivity activity,void*info) {MyClass*Object= (__bridge myclass*) info; Object->activity =activity;} Static voidRunloopobservercallback (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 Guarantee Synchronization//Create an observerCfrunloopobservercontext context = {0, (__bridgevoid*) Self,null,null}; Runloopobserver=cfrunloopobservercreate (Kcfallocatordefault, Kcfrunloopallactivities, YES,0, &Runloopobservercallback,&context); //adding observers to the common mode of the main thread RunloopCfrunloopaddobserver (Cfrunloopgetmain (), Runloopobserver, kcfrunloopcommonmodes); //creating child thread monitoringDispatch_async (Dispatch_get_global_queue (0,0), ^{ //the child thread opens a continuous loop for monitoring while(YES) {Longsemaphorewait = dispatch_semaphore_wait (Dispatchsemaphore, Dispatch_time (Dispatch_time_now, -*nsec_per_msec)); if(Semaphorewait! =0) { if(!runloopobserver) {Timeoutcount=0; Dispatchsemaphore=0; Runloopactivity=0; return; } //two runloop states, Beforesources and afterwaiting the two state interval time can detect whether the lag if(runloopactivity = = Kcfrunloopbeforesources | | runloopactivity = =kcfrunloopafterwaiting) { //appeared three times the result if(++timeoutcount3) { Continue; } //put the code for the stack information escalation server here } //End Activity}//End semaphore WaitTimeoutcount =0; }//End While }); }
Sometimes it is because of abnormal data, too much, or too large, or the abnormal appearance of the operation, such a situation may be difficult to meet in the usual daily development test, but in the real, especially the user audience is wide, people will appear, so this collection of the way is still valuable.
Method of Stack Dump
The first is to call the system function directly to get the stack information, this method can only get the simple information, unable to cooperate with dSYM to obtain the specific line of code out of the problem, the type is limited. The main idea of this method is to get the wrong signal signal. The code is as follows
Static intS_fatal_signals[] ={SIGABRT, Sigbus, SIGFPE, Sigill, SIGSEGV, SIGTRAP, SIGTERM, SIGKILL,};Static intS_fatal_signal_num =sizeof(s_fatal_signals)/sizeof(s_fatal_signals[0]); voidUncaughtexceptionhandler (NSException *exception) {Nsarray*exceptionarray = [exception callstacksymbols];//get the current call stack informationNSString *exceptionreason = [exception reason];//is very important, is the cause of the collapseNSString *exceptionname = [exception name];//Exception Type} voidSignalhandler (intcode) {NSLog (@"Signal handler =%d", code);} voidInitcrashreport () {//system error Signal capture for(inti =0; I signal (s_fatal_signals[i], signalhandler); } //capture of OC uncaught exceptionNssetuncaughtexceptionhandler (&uncaughtexceptionhandler);}intMainintargcChar*argv[]) {@autoreleasepool {initcrashreport (); returnUiapplicationmain (argc, argv, Nil, nsstringfromclass ([appdelegateclass])); }}
Reports that use plcrashreporter appear to be able to pinpoint the location of the problem code.
NSData *lagdata = [[[Plcrashreporter alloc] *lagreport =*lagreportstring = [ Plcrashreporttextformatter Stringvalueforcrashreport:lagreport Withtextformat:plcrashreporttextformatios]; // uploading a string to a server NSLog (@ "lag happen, detail below:%@", lagreportstring);
Some ways to detect APP performance in IOS