We all know that RunLoop is the key to keeping iOS applications running all the time, but what exactly is RunLoop? Let’s talk about it today.

RunLoop definition

  • A RunLoop, as the name suggests, is a loop that does something to keep the program running.

  • Functions: RunLoop is used for event response, gesture recognition, UI refresh, Timer, Autoreleasepool, PerformSelector, GCD Async Main Queue, network requests, etc.

Each thread has a unique RunLoop object corresponding to it

The RunLoop for the main thread is enabled by default (to keep the program running), and the RunLoop for the child thread is disabled by default (to be enabled when the thread first obtains a RunLoop).

The RunLoop is destroyed as the thread terminates

Runloops are stored in a global dictionary and are retrieved using threads as keys

Foundation of the RunLoop

  • Create the RunLoop
 NSRunLoop *mainRunLoop = [NSRunLoop mainRunLoop];
 NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];
Copy the code
  • mode
NSRunLoopMode model; NSDefaultRunLoopMode; // Default mode UITrackingRunLoopMode; //scrollView scrolls with this mode NSRunLoopCommonModes; // Take care of both modesCopy the code

The Core Foundation of RunLoop

  • Create the RunLoop
 CFRunLoopRef mainRL = CFRunLoopGetMain();
 CFRunLoopRef currentRL = CFRunLoopGetCurrent();
Copy the code
  • mode
CFRunLoopMode mode;

kCFRunLoopDefaultMode;
kCFRunLoopCommonModes;
Copy the code

NSRunLoop is a layer of OC encapsulation for CFRunLoopRef.

  • Listen to RunLoop, only c function interface

Monitoring Method 1

CFRunLoopObserverRef Observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observerRunLoopActivities, NULL); //2. Add observer CFRunLoopAddObserver(CFRunLoopGetMain(), Observer, kCFRunLoopCommonModes); //3. Release observer CFRelease(observer);Copy the code
/ / to monitor function void observerRunLoopActivities (CFRunLoopObserverRef observer, CFRunLoopActivity activity, Void *info){switch (activity) {case kCFRunLoopEntry:// RunLoop NSLog(@"kCFRunLoopEntry"); break; Case kCFRunLoopBeforeTimers: / / to handle the timer NSLog (@ "kCFRunLoopBeforeTimers"); break; Case kCFRunLoopBeforeSources: / / to handle the source NSLog (@ "kCFRunLoopBeforeSources"); break; Case kCFRunLoopBeforeWaiting: / / is about to enter dormancy NSLog (@ "kCFRunLoopBeforeWaiting"); break; Case kCFRunLoopAfterWaiting: / / just wake up from hibernation NSLog (@ "kCFRunLoopAfterWaiting"); break; Case kCFRunLoopExit:// about to exit RunLoop NSLog(@"kCFRunLoopExit"); break; / / case kCFRunLoopAllActivities: / / all the activities / / NSLog (@ "kCFRunLoopAllActivities"); // break; default: break; }}Copy the code

Listening method 2 (listening via block)

/ / 1. Create the observer CFRunLoopObserverRef observer2 = CFRunLoopObserverCreateWithHandler (kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {switch (activity) {case kCFRunLoopEntry:// Enter RunLoop NSLog(@"kCFRunLoopEntry"); break; // Other states... Omit case kCFRunLoopExit:// about to exit RunLoop NSLog(@"kCFRunLoopExit"); break; default: break; }}); //2. Add observer CFRunLoopAddObserver(CFRunLoopGetMain(), observer2, kCFRunLoopCommonModes); //3. Release observer CFRelease(observer2);Copy the code

Five classes for RunLoop in Core Foundation

CFRunLoopRef RunLoop
CFRunLoopMode RunLoop Running mode
CFRunLoopSourceRef Source1 source0,
CFRunLoopTimerRef The timer timer
CFRunLoopObserverRef Observer – Listen for RunLoop
  • CFRunLoopMode(NSRunLoopMode)

A RunLoop contains several Mode, each Mode and contains several Source0 / Source1 / Timer/Observer

Only one of these modes can be selected as currentMode when RunLoop starts

If you need to switch Mode, can only exit from the current Loop, choose a Mode to enter again (different groups Source0 / Source1 / Timer/Observer can be separated, each other)

If there is no any Source0 / Source1 / Timer Mode/Observer, RunLoop immediately exit

  • CFRunLoopObserverRef

The observer can listen for the active status of the RunLoop as follows

CFRunLoopActivity Indicates the activity status

KCFRunLoopEntry // About to enter RunLoop kCFRunLoopBeforeTimers // About to process timer kCFRunLoopBeforeSources // About to process source KCFRunLoopBeforeWaiting // About to enter sleep kCFRunLoopAfterWaiting // Just woke up from sleep kCFRunLoopExit // About to exit RunLoop kCFRunLoopAllActivities // All activitiesCopy the code

RunLoop runs the logic

RunLoop runs the logic
01. Notify Observers: enter Loop
Observers: They are about to deal with Timers
Observers: Intentions to deal with Sources
04, Process Blocks
05, processing Source0 (possibly processing Blocks again)
06, If Source1 exists, go to Step 8 — — — — — > 6 to 8
30, Notifying Observers that they are about to start hibernating.
30, notifying Observers that they are astounded by a message. <—– jumps from 06 to 08
– 01 > deal with the Timer
–02> GCD Async To Main Queue
Source1-03 > processing
09. Process Blocks
10. According to the previous results, decide what to do
–01> Return to Step 02
– 02 > exit the Loop
Observers: exit Loop
  • Source0:
Touch event handling performSelector: onThread:Copy the code
  • Source1:
System event capture for inter-thread communication based on PortCopy the code

For example, source1 captures click events and Source0 handles click events

  • Timers
NSTimer
performSelector:withObject:afterDelay:
Copy the code
  • Observers
Listen for state UI refresh of RunLoop (BeforeWaiting sleep) Autoreleasepool (BeforeWaiting sleep)Copy the code

RunLoop application

  • Fixed NSTimer stopping when sliding
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
Copy the code
  • Thread to keep alive

Encapsulates a thread – preserving utility class

FRPermenantThread.h

#import <Foundation/Foundation.h> typedef void (^myTask) (void); @interface FRPermenantThread: NSObject /** Execute tasks in child threads */ - (void)executeTask:(myTask)task; /** stop */ - (void)stop; @endCopy the code

FRPermenantThread.m

#import "frpermenantThread. h" //---------- / NSThread //@end // //@implementation FRThread //- (void)dealloc //{ // NSLog(@"%s",__func__); //} //@end //----------- @interface FRPermenantThread () @property (nonatomic, strong) NSThread *thread; @property (nonatomic, assign, getter=isStoped) BOOL stoped; @end @implementation FRPermenantThread #pragma mark - (instanceType)init {self = [super init]; if (self) { self.stoped = NO; __weak typeof(self) weakSelf = self; Self. Thread = [[NSThread alloc] initWithBlock:^{// NSLog(@" the child thread starts the task --"); // Add port timer source to wake up runloop [[NSRunLoop] currentRunLoop] addPort:[[NSPort alloc] init] forMode: NSDefaultRunLoopMode]; // [[NSRunLoop currentRunLoop] run];// Call run while (weakSelf &&! Weakself. isStoped) { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } // NSLog(@" child task flow ends --");}]; // Start the child thread [self.thread start]; } return self; } // executeTask in child thread - (void)executeTask:(myTask)task{if (! self.thread && ! task) return; [self performSelector:@selector(__doSomeThing:) onThread:self.thread withObject:task waitUntilDone:NO]; } - (void)stop{ if (! self.thread) return; [self performSelector:@selector(__stop) onThread:self.thread withObject:nil waitUntilDone:YES]; } #pragma mark - private interface pragma mark Otherwise CFRunLoopGetCurrent may fetch other threads - (void)__stop{self.stoped = YES; CFRunLoopStop(CFRunLoopGetCurrent()); self.thread = nil; } - (void)__doSomeThing:(myTask)tast{ tast(); } - (void)dealloc { [self stop]; // NSLog(@"%s",__func__); } @endCopy the code
  • Monitor performance lag

You can add an Observer to the main RunLoop to monitor the lag by listening for the time it takes for the RunLoop state to change

  • Optimize performance