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