2021.2 @ Hanniya

Recently, I have a lot of learning to prepare for the interview. RunLoop, Runtime, AutoreleasePool, RunLoop, RunLoop, RunLoop, AutoreleasePool

Interview Outline

What is the

  • Is a running loop mechanism that continuously schedules various tasks within a thread.

What do you do

  1. performTask()Execute tasks: Block, Source0, Source1, Main queue, Timer
  2. callout_to_observer()External notification: Activity, Source0, and Timer
  3. sleep()sleep

Application: Timer, thread survival, lag detection

1. The RunLoop profile

1.1 role

  • To keep the program running:

Once the program is started, UIApplicationMain opens a main thread and runs a RunLoop corresponding to the main thread. This RunLoop ensures that the main thread is not destroyed, thus ensuring that the program continues to run.

  • Handle various events in the App, such as touch events, timer events, Selector events, etc
  • Save CPU resources and improve program performance

When there’s nothing to do, the RunLoop tells the CPU to rest, freeing up its resources to do something else, and when there’s something to do, the RunLoop does it

1.2 the characteristics of

  • Relationship with threads

A one-to-one correspondence between a thread and a RunLoop is stored in a global Dictionary, with the thread as key and RunLoop as value.

  • The life cycle

The RunLoop of the child thread is created on the first fetching (if the child thread is not actively fetching, it is not created, which can be interpreted as lazy loading), and the RunLoop is destroyed at the end of the thread.

  • To obtain

You can only get its RunLoop inside a thread (other than the main thread).

//Foundation
[NSRunLoop currentRunLoop]; // Get the current thread's RunLoop object
[NSRunLoop mainRunLoop]; // Get the RunLoop object for the main thread

//Core Foundation
CFRunLoopGetCurrent(a);// Get the current thread's RunLoop object
CFRunLoopGetMain(a);// Get the RunLoop object for the main thread
Copy the code

NSRunLoop is a layer of CFRunLoopRef that encapsulates CFRunLoopRef’s API and is thread safe; NSRunLoop provides object-oriented apis, but these apis are not thread-safe.

A RunLoop is created when a child thread is opened, not by alloc init, but directly by calling currentRunLoop, as it is a lazy load.

2. What does RunLoop do

2.1 performTask() Executes a task

DoBlocks()

  • Developers can use

DoSources0()

  • Developers can use
  • Source 0 cannot wake up RunLoop

DoSources1()

  • System only
  • Source 1 can proactively wake up RunLoop
  • Based on the mach_msg function, the task to execute is determined by reading messages from the kernel message queue on the port.
  • Tasks include rendering UI and so on

DoMainQueue()

  • Developers can use the GCD API to put tasks in the main Queue

DoTimers()

  • Developers can use, call the NSTimer API to register the task being performed

2.2 callout_to_observer() Notifyexternal

Doobservers-activity Current state

typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),        // Each time you enter Runloop (e.g. after switching mode)
    kCFRunLoopBeforeTimers = (1UL << 1), / / the DoTimers
    kCFRunLoopBeforeSources = (1UL << 2),/ / the DoSources
    kCFRunLoopBeforeWaiting = (1UL << 5),// The current thread is about to go to sleep (if there are no more messages in the current queue)
    kCFRunLoopAfterWaiting = (1UL << 6), // The current thread recovers from sleep (read the queue message and continue execution)
    kCFRunLoopExit = (1UL << 7),         / / from the Runloop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};
Copy the code

DoObservers-Timer

Indicates that the Timer will be processed

DoObservers-Source0

Indicates that Source0 will be processed

2.3 the sleep () to sleep

2.4 Comprehensive Flow chart

Each loop of a Runloop does not always execute the various performTasks and callout_to_observer above in sequence, but rather mixes the jumps together

To understand the complete process, borrow mrPeak’s diagram:

  • Poll: If the source0 task is processed, the Poll value is true and no notifications are made before or after sleep.
  • DoBlocks – > DoSource0 – > (sleep) – > DoSource1 / DoMainQueue/DoTimers – > DoBlocks cycle

Sleep wake up after the RunLoop DoSource1 DoMainQueue/DoTimers will only choose three

3. The principle of RunLoop

3.1 Essence: structure

RunLoop structure

struct __CFRunLoop {.// Omit non-core members
	  CFMutableSetRef _commonModes; 
    CFMutableSetRef _commonModeItems; 
    CFRunLoopModeRef _currentMode; // A pointer to the _CFRunLoopMode structure
    CFMutableSetRef _modes; // Multiple mode arrays
};
Copy the code

Mode structure

typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {.// Omit non-core members
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
};
Copy the code
  • A RunLoop contains multiple modes, and each Mode contains multiple sources, timers, and observers

MainQueue The execution of the task is independent of the mode. The mode has no related information

3.2 Mode: CFRunLoopModeRef

Mode is divided into Common Mode and Private Mode. Therefore, the observer does not monitor all runloops

  • RunLoop Mode switchover

RunLoop starts by selecting one of the modes as currentMode; If you need to switch Mode, you can only exit the RunLoop and specify a new Mode to enter. This is mainly to separate Source, Timer, and Observer groups from each other. If there is no Source/Timer/Observer in the current Mode, RunLoop doesn’t idle, it exits immediately.

3.3 the Source/Timer/Observer (0/1)

  • Source Event generation
    • Source0: contains a function pointer (callback) that accepts events that are triggered outside of the RunLoop. The RunLoop cannot be woken up actively. Events (touch events, performSelectors) can only be woken up via the Wakeup interface.
    • Source1: contains a mach_port and a function pointer (callback) to actively wake up RunLoop (port-based inter-thread communication)
  • 12. Timer: Timer containing a length of time and a function pointer (callback)
  • Observer: An Observer that contains a function pointer (callback) that listens for the state of the RunLoop

3.4 Memory management of RunLoop

When entering RunLoop, the main thread RunLoop creates an AutoreleasePool when the kCFRunLoopEntry state is observed through the Observer.

4. Interview questions

4.1 Does NSTimer respond when dragging a TableView without processing? How to solve

No response.

The reason: By default, NSTimer will only schedule to kCFRunLoopDefaultMode. When scrollView slides, runloop will enter UITrackingRunLoopMode. Naturally, the NSTimer task will not be triggered by the doTimer

The solution:

  1. Add NSTimer to UITrackingRunLoopMode as well (but so that the timer is added twice, not the same timer)
  2. Adding NSTimer to NSRunLoopCommonModes marks itself as Common, and all modes that are also marked as Common will continue to process this event.

Even so, there is a problem of not executing the Timer when RunLoop uses system private mode.

[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
Copy the code

4.2 Which is more accurate, NSTimer or GCD? why

CGD timer is more accurate. because

  1. NSTimer is a Runloop that checks whether the time is up or not. There is an error.

RunLoop saves resources by not calling back this Timer at a very precise point in time. Timer has an attribute called Tolerance, which indicates how much maximum error is allowed when the time point is up. If a point in time is missed, such as when a long task has been executed and the Timer’s tolerance has passed, the callback for that point will also be skipped without delay.

  1. NSTimer may be delayed due to Mode problems.
    // Create a queue
    dispatch_queue_t queue = dispatch_get_global_queue(0.0);
    //1. Create a GCD timer
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0.0, queue);
    // A strong reference to the timer is required to ensure that the timer is not released before the block is called on time
    // Local variable, make the pointer strong reference
    self.timer = timer;
    //2. Set the start time, interval time and accuracy of the timer
    // The accuracy is generally 0. Increasing the error within the allowable range can improve the performance of the program
    //GCD is in nanoseconds, so * NSEC_PER_SEC
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC.0 * NSEC_PER_SEC);
    //3. Set the timer to execute
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@ "-- - % @ -"[NSThread currentThread]);
    });
    dispatch_resume(timer); / / start
Copy the code

4.3 How does RunLoop Respond to user events, gestures, and Interface refreshes

  • UIEvent Event History:

Finger touch screen

  1. The IOKit. Framework encapsulates events as IOHIDEvent objects
  2. Port communication: forward to APP through Mach port and receive by Source1 in Runloop
  3. Runloop callback (Source1 callback -> Source0)
  4. Callbacks to Source0 add touch events to the event queue (FIFO)
  5. UIApplication starts looking for the best responder when exiting the queue (hit-testing)
  6. The event is sent to the best responder for response or transmission
  • Gestures:

System registered an Observer monitoring BeforeWaiting (RunLoop entering hibernation) events, the callback function Observer is _UIGestureRecognizerUpdateObserver (), Internally it gets all the GestureRecognizer that was just marked for processing and executes the GestureRecognizer’s callback. This callback handles any changes (create/destroy/state change) to the UIGestureRecognizer.

  • Interface:

When in operation the UI, such as changing the Frame, update the UIView/CALayer level, or manually invoked the UIView/CALayer setNeedsLayout/setNeedsDisplay method, The UIView/CALayer is marked as unprocessed and submitted to a global container. The system registers an Observer to listen for BeforeWaiting(about to sleep) and Exit (about to Exit Loop) events, calls back to execute the function, which iterates through all UIView/CAlayer to perform the actual drawing and tuning, and updates the UI.

  • CADisplayLink

Think of it as a timer that matches the refresh rate of the screen. If you perform a long task between screen flushes, one frame will be skipped, causing the interface to feel stuck. Even a frame of lag when sliding a TableView quickly will be noticeable to the user.

4.4 Application of RunLoop in third-party Libraries

  • AFNetworking

AFURLConnectionOperation the AFURLConnectionOperation class is built on NSURLConnection and is expected to receive a Delegate callback in a background thread. AFNetworking creates a separate thread and starts a RunLoop in that thread.

  • AsyncDisplayKit

Facebook’s framework for keeping the interface fluid puts drawing and typography in background threads. Use Node to encapsulate View and Layer and implement a similar set of interface update mechanisms: Add an Observer to the RunLoop of the main thread that listens for the kCFRunLoopBeforeWaiting and kCFRunLoopExit events, and when the callback is received, iterates over all the pending tasks that have been queued before and executes them one by one.

5. A selection of articles to review about RunLoop

Reference:

Decryption Runloop iOS learning – introduction to Runloop – cloud + community – tencent cloud Deep understanding of RunLoop | Garan no dou iOS underlying principle summary RunLoop — the Denver nuggets

Source: CFRunLoop. C