1. How does Runloop relate to threads?

  • There is one Runloop for each thread.

  • The main thread has Runloop by default.

  • Runloops for child threads are created as lazy loads.

  • Runloops are stored in a global mutable dictionary where threads are keys and runloops are values.

2.RunLoop running mode

There are five RunLoop modes. RunLoop only runs in one mode. To switch modes, pause the current mode and restart another mode

  • KCFRunLoopDefaultMode, the default running mode of the App, in which the main thread usually runs
  • UITrackingRunLoopMode, tracking user interaction events (used for ScrollView to track touch sliding, ensuring interface sliding is not affected by other modes)
  • Kcfrunloopcommonmode, pseudo-mode, is not a true operating mode
  • UIInitializationRunLoopMode: in the first to enter the first Mode when just start the App, start after the completion of the will no longer be used
  • GSEventReceiveRunLoopMode: accept system internal event, is usually not used

3. Internal runloop logic?

RunLoop is actually just such a function, with a do-while loop inside. When you call CFRunLoopRun(), the thread stays in the loop forever; This function does not return until it times out or is stopped manually.

Internal logic:

1. Notify the Observer that RunLoop has been entered. Notify the Observer that a Timer is about to be processed 3. Notify the Observer that a non-port-based input source is about to be processed (Source0) 4. Work with non-port-based input sources that are ready (work with Source0) 5. If the port-based input source is ready and waiting for processing, handle the event immediately. Go to step 9 (handling Source1) 6. Notify the Observer that the thread is going to sleep 7. Put the thread to sleep, Until one of the following events occurs * the event reaches the port-based input sources (Source0) * Timer runs out of time * External manual wake up * The time set for RunLoop times out 8. 9. Handle pending events * If it is a Timer event, process the Timer and restart the loop, skip to step 2 * If the input source is fired, If RunLoop is manually awakened but has not yet timed out, restart the loop and skip to step 2Copy the code

4. When is autoreleasePool released?

  • The App starts, apple registered in the main thread RunLoop two Observer, the callback is _wrapRunLoopWithAutoreleasePoolHandler ().

  • The first Observer monitors an event called Entry(about to enter Loop), which creates an automatic release pool within its callback by calling _objc_autoreleasePoolPush(). Its order is -2147483647, the highest priority, ensuring that the release pool is created before all other callbacks.

  • The second Observer monitors two events: calling _objc_autoreleasePoolPop() and _objc_autoreleasePoolPush() while waiting (ready to sleep) torelease the old pool and create a new one; _objc_autoreleasePoolPop() is called upon Exit(about to Exit the Loop) torelease the automatic release pool. The order of this Observer is 2147483647, the lowest priority, ensuring that its release pool occurs after all other callbacks.

  • The code that executes on the main thread is usually written inside such callbacks as event callbacks and Timer callbacks. These callbacks are surrounded by AutoreleasePool created by RunLoop, so there is no memory leak and the developer does not have to show that the Pool was created.

5.GCD in Runloop?

  • GCD is returned from the child thread to the main thread, and RunLoop is only triggered in this case. The Source 1 event of the RunLoop is raised.

6. How to use Runloop in AFNetworking?

  • The AFURLConnectionOperation class is built on NSURLConnection and is expected to receive Delegate callbacks in the background thread. AFNetworking creates a separate thread for this purpose and starts a RunLoop in this thread:
+ (void)networkRequestThreadEntryPoint:(id)__unused object {
    @autoreleasepool {
        [[NSThread currentThread] setName:@"AFNetworking"];
        NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
        [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
        [runLoop run];
    }
}

+ (NSThread *)networkRequestThread {
    static NSThread *_networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [_networkRequestThread start];
    });
    return _networkRequestThread;
}
Copy the code
  • Before RunLoop starts, there must be at least one Timer/Observer/Source, so AFNetworking creates a new NSMachPort and adds it to it before [RunLoop run]. Normally, the caller needs to hold this NSMachPort (mach_port) and send messages inside the loop through this port from the external thread; But port is added here only to keep the RunLoop from exiting, not to actually send messages.
- (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread]  withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; }Copy the code
  • When you need the background thread, when performing a task AFNetworking by calling [NSObject performSelector: onThread:… Throw the task into the RunLoop of the background thread.

7. How does PerformSelector work?

  • When calling NSObject performSelecter: afterDelay: after its internal will actually create a Timer and add to the current thread RunLoop. So if the current thread does not have a RunLoop, this method will fail.

  • When the performSelector: onThread: when, in fact, it will create a Timer is added to the corresponding thread, in the same way, if the corresponding thread no RunLoop this method will fail.

8. PerformSelector: afterDelay: this method in the child thread work?

  • It doesn’t work. The child thread has no Runloop by default and therefore no Timer. This can be done using GCD’s dispatch_after

9. Event response process?

  • Apple registered a Source1 (based on the Mach port) to the receiving system, the callback function as __IOHIDEventSystemClientQueueCallback ().

  • When a hardware event (touch/lock/shake, etc.) occurs, an IOHIDEvent event is first generated by IOKit. Framework and received by SpringBoard. Details of this process can be found here. SpringBoard only receives events such as buttons (lock screen/mute etc.), touch, acceleration and proximity sensor, and then forwards them to the App process using Mach port. Then apple registered the Source1 will trigger the callback, and call the _UIApplicationHandleEventQueue () distribution within the application.

  • _UIApplicationHandleEventQueue () will wrap IOHIDEvent process, and as a UIEvent processing or distribution, including identifying UIGesture/processing screen rotation/send UIWindow, etc. Usually click event such as a UIButton, touchesBegin/Move/End/Cancel events are completed in the callback.

10. Gesture recognition process?

  • When _UIApplicationHandleEventQueue () to identify a gesture, the first call will Cancel the current touchesBegin/Move/End series callback to interrupt. The system then marks the corresponding UIGestureRecognizer as pending.

  • Apple registered a Observer monitoring BeforeWaiting (Loop entering hibernation) events, the callback function Observer is _UIGestureRecognizerUpdateObserver (), Internally it gets all GestureRecognizer that has just been marked for processing and executes the GestureRecognizer callback.

  • This callback is handled when there are changes to the UIGestureRecognizer (create/destroy/state change).

11. Which is more accurate, CADispalyTimer or Timer

More accurate CADisplayLink

  • IOS devices refresh their screens at a fixed rate, and CADisplayLink is normally called at the end of each refresh with a high degree of accuracy.

  • NSTimer is less precise. For example, when NSTimer is triggered, if runloop is blocked, the trigger time will be delayed to the next runloop cycle. And NSTimer has added a tolerance property that allows users to set a tolerance range of triggering time delays.

  • CADisplayLink can be used for more specific purposes, such as constant redrawing of UI, such as custom animation engines or rendering of video playback. NSTimer can be used for a much wider range of tasks that require single or cyclic timing. The advantage of CADisplayLink over NSTimer for UI-related animation or display content is that we don’t need to worry too much about the screen refresh rate, because it is already synchronized with the screen refresh rate.

This is the end of the article, thank you for watching, just something to say to readers:

If you are interviewing for a job, or are planning to change jobs, you might as well take a look at my carefully summarized interview materials: BAT Big factory latest interview questions + answers collection (constantly updated) to get a detailed interview materials for your job-hopping salary more than a guarantee.