1. WhyNSTimerSometimes it doesn’t work?

Since the created NSTimer is added to defaultMode by default, when the Mode of the Runloop changes

The current NSTimer will not work.

2. How to use Runloop in AFNetworking?

RunLoop must have at least one Timer/Observer/Source internally before starting, so AFNetworking created a new NSMachPort and added it before [RunLoop Run].

Typically, the caller needs to hold this NSMachPort (mach_Port) and pass through it on an external thread

Port Sends a message to the loop. But the port is added here only to keep the RunLoop from exiting, not to actually send the message.

When this background thread is needed to perform a task, AFNetworking is called

[NSObject performSelector:onThread:..] Throw the task into the RunLoop of the background thread

3. When is autoreleasePool released?

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

The first event monitored by the Observer is Entry (about to enter the Loop), which is called within the callback

_objc_autoreleasePoolPush() Creates an automatic release pool. Its order is -2147483647, which takes precedence

Highest level, ensuring that the creation of the release pool occurs before any other callbacks.

The second Observer monitors two events: called when waiting (ready to go to sleep)

Objc_autoreleasePoolPop () and objc_autoreleasePoolPush() release the old pool and create a new one; Call _objc_autoreleasePoolPop() when Exit (about to Exit the Loop) torelease the automatic release

Pool. The order of this Observer is 2147483647, which has the lowest priority, ensuring that its pool release occurs after all other callbacks.

Code that is executed on the main thread is usually written in event callbacks, Timer callbacks, etc. These callbacks will be

The RunLoop creates the AutoreleasePool around, so there are no memory leaks and the developer does not have to show that the Pool was created.

4. The PerformSelector: afterDelay: this method in the child thread work? Why is that? How to solve it?

No, the child thread has no Runloop by default, and therefore no Timer.

The solution can be implemented using GCD: Dispatch_after

5. The RunLoop Mode

First of all, as a developer, it is particularly important to have a learning atmosphere and a communication circle. This is my iOS development public account: Programming Daxin. No matter you are a small white or a big ox, you are welcome to enter. (The group will provide some free learning books collected by the group owner and hundreds of interview questions and answer documents!) 民运分子

The first thing to know about Mode is that a RunLoop object may contain more than one Mode, and only one Mode(CurrentMode) can be specified each time RunLoop’s main function is called. To switch Mode, you need to specify a new Mode. The main purpose is to separate different Source, Timer, Observer, so that they do not affect each other.

When a RunLoop is running on Mode1, it is not acceptable to process Source Timer Observer events on Mode2 or Mode3.

UITrackingRunLoopMode: track user interaction events (used for ScrollView tracking touch swiping, to ensure that the interface is not affected by other modes)

UIInitializationRunLoopMod: in the first to enter the first Mode when just start the App, launch is no longer used after the completion of GSEventReceiveRunLoopMode: KCFRunLoopCommonModes: a solution for synchronizing Source/Timer/Observer to multiple modes. KCFRunLoopCommonModes: a solution for synchronizing Source/Timer/Observer to multiple modes

6. Implementation mechanism of RunLoop

The core thing to do with RunLoop is to make sure that threads sleep when there are no messages and wake up when there are messages to improve performance. The RunLoop mechanism relies on the kernel (Mach in Darwin, the core component of Apple’s operating system).

RunLoop receives and sends messages through the mach_msg() function. Essentially, it calls the function mach_msg_trap(), which is a system call that triggers a kernel state switch. Calling mach_msg_trap() in user mode switches to kernel mode; The kernel implementation of the mach_msg() function in kernel mode does the actual work.

That is, based on the source1 of the port, listen to the port, the port will trigger a message callback; For source0, manually mark it as pending and manually wake up RunLoop

The rough logic is:

1. Notify observers that RunLoop is about to start.

2. Notifies the observer that the Timer event is about to be processed.

Inform observers that the Source0 event is about to be processed.

Process the source0 event.

5. If the port-based source (Source1) is ready and in the waiting state, go to Step 9.

6. Notify the observer that the thread is going to sleep.

7. Put the thread into hibernation state, switch from user mode to kernel mode, and wake up the thread until any of the following events occur.

(1) A Source1 event based on port (source0).

(2) A Timer is out of time.

(3) The timeout period of the RunLoop itself has expired.

(4) Be manually awakened by other callers.

Inform the observer that the thread will be awakened.

9. Handle events received upon awakening.

(1) If the user-defined timer starts, process the timer event and restart RunLoop. Go to Step 2.

(2) If the input source is started, deliver the corresponding message.

(3) If RunLoop is woken up and the time has not timed out, restart RunLoop. Go to Step 2

10. Notify the observer that the RunLoop is complete.

7. How to create a resident thread?

1. Enable a RunLoop for the current thread when the [NSRunLoop currentRunLoop] method is first called

It actually creates a RunLoop first.)

Add a Port/Source to the current RunLoop to maintain the RunLoop event loop (if there is no item in the mode of RunLoop, RunLoop will exit)

3. Start the RunLoop

8. Data structure of RunLoop

NSRunLoop(Foundation) is a wrapper of CFRunLoop(CoreFoundation) and provides an object-oriented API

There are five main classes related to RunLoop:

CFRunLoop: indicates a RunLoop object

CFRunLoopMode: indicates the running mode

CFRunLoopSource: input source/event source

CFRunLoopTimer: timing source

CFRunLoopObserver: indicates the observer

1, CFRunLoop

Composed of pthread (thread object, indicating that runloops and threads are one-to-one), currentMode(currentMode), modes (collection of multiple modes), commonModes (collection of mode name strings),

CommonModelItems (Observer,Timer,Source collection)

2, CFRunLoopMode

Consists of name source0 source1 Observers Timers

3, CFRunLoopSource

There are two types: source0 and source1

source0:

That is, non-port-based, that is, user-triggered events. You need to manually wake up the thread to switch the current thread from kernel mode to user mode

source1:

Port-based, containing a mach_port and a callback that listens for system ports and messages sent through the kernel and other threads, can proactively wake up the RunLoop to receive and distribute system events. Have the ability to wake up threads

4, CFRunLoopTimer

Time-based triggers, that’s basically NSTimer. Wake up the RunLoop at a preset point in time to perform the callback. Because it is based on RunLoop, it is not real-time (i.e., NSTimer is inaccurate). Because RunLoop is only responsible for distributing messages from the source. If the thread is currently processing heavy tasks, it may cause the Timer to delay this time, or execute one less time.

5, CFRunLoopObserver

Listen for the following point in time: CFRunLoopActivity

KCFRunLoopEntry: RunLoop is ready to start

KCFRunLoopBeforeTimers: RunLoop will process some timer-related events

KCFRunLoopBeforeSources: RunLoop will handle some Source events

KCFRunLoopBeforeWaiting: RunLoop is going to sleep, which is to switch from user mode to kernel mode

KCFRunLoopAfterWaiting: The RunLoop is woken up, that is, after the kernel mode is switched to user mode

KCFRunLoopExit: indicates that the RunLoop exits

KCFRunLoopAllActivities: listens for all states

6. Relationships between data structures

Threads correspond to runloops one to one. RunLoop and Mode are one-to-many, and Mode is one-to-many with source, timer, and observer

9. ExplainNSTimer

NSTimer is actually CFRunLoopTimerRef and toll-free bridged between them. When an NSTimer is registered with a RunLoop, the RunLoop registers events for its repeated point in time. For example,

10:00, 10:10, 10:20. 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, for example by a long task, the callback for that point is skipped without delay. Like waiting for the bus, if I’m too busy on my phone at 10:10 and MISS the bus at that time, THEN I have to wait for the 10:20 bus.

CADisplayLink is a timer that is consistent with the refresh rate of the screen (although the actual implementation is more complicated, unlike NSTimer, which actually operates a Source inside). If a long task is performed between screen flusher, one frame will be skipped (similar to NSTimer), causing the interface to feel stuck. Even a frame of lag when sliding a TableView quickly will be noticeable to the user.

Facebook’s open source AsyncDisplayLink is designed to solve this problem and uses RunLoop internally

10. Explain the 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 generated by the IOKit. Framework and received by SpringBoard. The details of this process can be found here.

SpringBoard only receives events such as button (lock screen/mute, etc.), touch, acceleration, proximity sensor, etc., and then forwards them to the desired 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 identification of UIGesture / / UIWindow, etc. Usually click event such as a UIButton, touchesBegin/Move/End/Cancel events are completed in the callback.

11. Explain the gesture recognition process?

When the above _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 an Observer to monitor BeforeWaiting (Loop about to go to sleep) events, this

The Observer callback function is _UIGestureRecognizerUpdateObserver (), its internal will obtain all just marked as pending GestureRecognizer, and implement GestureRecognizer callback.

This callback handles any changes (create/destroy/state change) to the UIGestureRecognizer.

12. Explain the page rendering process using runloop?

When we call [UIView setNeedsDisplay], the current view.layer method of [view.layer setNeedsDisplay] is called.

This marks the current layer as dirty, and no work is being done. Instead, drawing is done when the current Runloop is about to sleep, that is, beforeWaiting.

The CALayer display is then called, and the actual drawing is done. The CALayer layer determines whether its delegate implements a displayer: method, which is the entry point for the asynchronous drawing. If it does not implement this method, the system drawing process will continue and the drawing will be finished.

The CALayer will create a Backing Store inside the CALayer to obtain the graphics context. The next step is to determine if the layer has a delegate.

If so, [layer.delegate drawLayer:inContext:] will be called and will return a callback to [UIView DrawRect:] to let us do something on top of the system drawing.

If there is no delegate, then [CALayer drawInContext:] is called.

For the above two branches, CALayer will submit the bitmap to the Backing Store and finally to the GPU. This is the end of the drawing process.

13. What is asynchronous drawing?

Asynchronous drawing, that is, you can draw the graph in the child thread, in advance of the child thread processing. The prepared image data is returned directly to the main thread for use, thus reducing the pressure on the main thread.

The process of drawing asynchronously

The system’s [View. delegate displayLayer:] entry is used for asynchronous drawing.

The agent is responsible for generating the corresponding Bitmap

Set the Bitmap to the value of the layer.contents property

That’s all for this share, thanks for watching!