RunLoop summary and interview: RunLoop summary and interview: RunLoop summary and interview: RunLoop summary and interview: RunLoop summary and interview: RunLoop summary and interview

1. Look for caton entry points

Monitoring Caton, which is basically finding out what the main thread is doing. We know that a thread’s message event processing is driven by NSRunLoop, so to know what method the thread is calling, we need to start with NSRunLoop.

The code to execute RunLoop looks like this:

Observers create AutoreleasePool: _objc_autoreleasePoolPush(); /// Observers create AutoreleasePool: _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);do{/// 2. Notify Observers that a Timer callback is about to occur. __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers); Notifying Observers that Source (non-port-based,Source0) callback is about to occur. __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// 4. Trigger the Source0 (non-port-based) callback. __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0); __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block); /// Observers are in this state torelease and create AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting); /// 7. sleep towaitmsg. mach_msg() -> mach_msg_trap(); Observers, __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting); /// 8. /// 9. If the Timer wakes up, call Timer __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(Timer); /// 9. Block __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block); __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
 
 
    } while(...). ; Observers are about torelease AutoreleasePool: _objc_autoreleasePoolPop(); __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit); }Copy the code

As can be seen from the above, RunLoop processes events in two phases:

  • Between kCFRunLoopBeforeSources and kCFRunLoopBeforeWaiting
  • After kCFRunLoopAfterWaiting

2. The RunLoop function

We can use the CFRunLoopObserverRef to monitor the state of NSRunLoop, which allows us to obtain changes in these state values in real time.

  1. CFRunLoopObserverContext Context = {0, (__bridge void *)self, NULL, NULL};

  2. MyRunLoopObserver creates a Runloop Observer. The first parameter is used to allocate memory for the observer. The second parameter is used to set the events that the observer is interested in. Used to identify the observer is in the first time into the runloop performed or every time into the runloop processing are performed The fourth parameter, is used to set the priority of the observer The fifth parameter: the callback function is used to set the observer The sixth parameter: CFRunLoopObserverCreate(<#CFAllocatorRef allocator#>, <#CFOptionFlags activities#>, <#Boolean repeats#>, <#CFIndex order#>, <#CFRunLoopObserverCallBack callout#>, <#CFRunLoopObserverContext *context#>)

  3. Add the new observer to the runloop CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes) of the current thread.

  4. Remove observer from thread runloop CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes);

  5. The release of the observer CFRelease (_observer); _observer = NULL;

3. The semaphore

// Create semaphore with parameters: Dispatch_semaphore_create = NULL if less than 0 // Increase dispatch_semaphore_signalCopy the code

Note: The normal order of use is to lower and then raise; these two functions are usually used in pairs.

4. Quantify the degree of Caton

Principle: Observe the duration of various state changes of Runloop to detect the occurrence of delay in calculation. An effective delay adopts the judgment strategy of “N times of delay exceeding the threshold T”, that is, collection and report will be triggered only when the accumulative number of delay in a period is greater than N: For example, the delay threshold T=500ms and the delay times N=1 can be considered as the effective delay of a long time. However, the stalling threshold T=50ms and stalling times N=5 can be judged as the effective stalling with fast frequency

Practice: We need to start a child thread to calculate in real time whether the time between two state regions has reached a certain threshold. In addition, it is necessary to cover two scenarios of multiple consecutive small delays and single long delays.

static void runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) { MJMonitorRunloop *instance = [MJMonitorRunloop sharedInstance]; Instance ->_activity = activity; // dispatch_semaphore_t semaphore = instance->_semaphore; dispatch_semaphore_signal(semaphore); } // Register an Observer to monitor the status of the Loop. The callback function is runLoopObserverCallBack - (void)registerObserver {// Set the runtime environment of the Runloop Observer CFRunLoopObserverContext context = {0, (__bridge void *)self, NULL, NULL}; _observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, &runLoopObserverCallBack, &context); // Add the new observer to the current thread's runloop CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); _semaphore = dispatch_semaphoRE_create (0); __weak __typeof(self) weakSelf = self; // Dispatch_async (dispatch_get_global_queue(0, 0), ^{__strong __typeof(weakSelf) strongSelf = weakSelf;if(! strongSelf) {return;
        }
        while (YES) {
            if (strongSelf.isCancel) {
                return; } // If the number of dispatch_semaphore_wait(self->_semaphore, dispatch_time(DISPATCH_TIME_NOW, strongSelf.limitMillisecond * NSEC_PER_MSEC));if(dsw ! = 0) {if (self->_activity == kCFRunLoopBeforeSources || self->_activity == kCFRunLoopAfterWaiting) {
                    if (++strongSelf.countTime < strongSelf.standstillCount){
                        NSLog(@"%ld",strongSelf.countTime);
                        continue;
                    }
                    [strongSelf logStack];
                    [strongSelf printLogTrace];
                    
                    NSString *backtrace = [MJCallStack mj_backtraceOfMainThread];
                    NSLog(@"+ + + + % @",backtrace);
                    
                    if(strongSelf.callbackWhenStandStill) { strongSelf.callbackWhenStandStill(); } } } strongSelf.countTime = 0; }}); }Copy the code

Test cases

Using a tableView, drag up and down to artificially stall (sleep) to test our real-time monitoring of sleepy code.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString  *identify =@"cellIdentify";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identify];
    if(! cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identify]; }if(indexPath.row % 10 == 0) { usleep(1 * 1000 * 1000); // 1 second cell.textLabel.text = @"Carlo";
    }else{
        cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
    }
    
    return cell;
}
Copy the code

6. Record card data

When the blockage is detected, the stack information is captured and some filtering is done on the client. (Debug) can be saved locally and (Release) can be uploaded to the server. After collecting a certain amount of blockage data, it can accurately locate the places to be optimized after analysis.

Once the stack information is obtained, it can be parsed using the MJCallStack class in Demo (see BSBacktraceLogger – Lightweight call stack analyzer) or KSCrash, PLCrashReporter, etc.

At this point the real-time caton monitoring is done. GitHub address: MJRunLoopDemo

Reference article:

Simple monitoring iOS backtracelogger RunLoop monitor BSBacktraceLogger RunLoop summary and interview with dispatch_semaphore (semaphore) understanding and use