1. Working principle of KVC

Key-value Coding is a mechanism enabled by the NSKeyValueCoding informal protocol for objects to provide indirect access to their attributes/member variables.

KVC setting principle:KVC value principle:

Does modifying properties via KVC trigger KVO?

Is triggered. Modifying the value of an object property via KVC always fires KVO regardless of whether the setKey method is called. This is because KVC calls the object’s willChangeValueForKey: and didChangeValueForKey: methods when modifying the value of an object’s property, KVO is triggered whenever these methods are called.

2. Working principle of KVO

KVO (Automatic key observation) is implemented through ISA-Swizzling (swapping). The basic process is that the compiler automatically creates a derived class for the observed (the parent of which is the class to which the observed belongs) and points the observed’s ISA to this derived class (class name: NSKVONotifying_XXX). If the user registers an observation for a property of the target object, the derived class overrides the setter method for that property and adds notification code to it. When objective-C sends a message, it uses the ISA pointer to find the class object to which the current object belongs. The class object holds the instance method that the current object can call, so when you send a message to this object, you are actually sending a message to the method of the derived object. Because the compiler overrides the methods of the derived class and adds notification code, notifications are sent to registered observer objects. Note that derived classes override only the property methods that register the observer.

3. Method cache, fast search, slow search and message forwarding process in iOS

struct objc_class { Class isa; Class superclass; cache_t cache; // Method cache class_data_bits_t bits; // To get specific class information};Copy the code

The Class internal structure has a method cache called cache_t, which uses hash tables (hash tables) to cache previously called methods to speed up method lookups.

struct cache_t { struct bucket_t *_buckets; // hash mask_t _mask; // The length of the hash table is -1 mask_t _occupied; // The number of methods already cached}; struct bucket_t { cache_key_t _key; //SEL as key IMP _imp; // Function memory address}Copy the code

How is objc_msgSend implemented?

At first glance it looks like a C/C++ function, but it’s actually implemented in assembly. The reason for using assembly, besides being fast and having a lot of method look-up operations, is that assembly is easier for machines to recognize than the underlying language, and there are other important reasons for saving some of the compilation process in the middle. Assembly is implemented to deal with different Calling conventions. The stack and register parameters and state Settings before a function call are handed over to the compiler. Quickly find

Slow to find

Message Forwarding Process

Simplified version of messaging mechanism

IOS response chain and event delivery

Responder chain: A chain composed of multiple responders, called a responder chain. It represents the relationship between each responder and makes it possible for an event to select multiple objects for processing

How to judge the previous responder:

  • 1. If the current view is the view of the controller, then the controller is the last responder
  • 2, if the current view is not the view of the controller, then the parent control is the previous responder

Event transfer process of responder chain:

  • 1, if the current view is the view of the controller, then the controller is the last responder, the event is passed to the controller; If the current view is not the view of the controller, then the parent view is the last responder of the current view and the event is passed to its parent view
  • 2. The top-level view in the view hierarchy passes the event or message to the Window object for processing if it cannot process the event or message it receives
  • 3. If the Window object does not process either, it passes the event or message to the UIApplication object
  • 4. If UIApplication cannot handle the event or message, it is discarded

Event passing process:

  • When the iOS system detects the Touch operation, it will package it into a UIEvent object and put it into the event queue of the current active Application. The UIApplication of the singleton will take out the Touch event from the event queue and pass it to the UIWindow of the singleton for processing. The UIWindow object first uses the hitTest:withEvent: method to find the View at which the Touch operation started. That is, it needs to pass the Touch event to the View it handles (the most appropriate control to handle). This process is called hit-test View.

Two important response methods (UIView)

  • Hit-test view: This method is called when the event is passed to the control to find the most appropriate view and return the view that can respond
  • PointInside: This method determines whether the touch point is on the control and returns YES if it is, and NO otherwise. The point argument must be the method caller’s coordinate system.

6. The difference between UIView and CALayer

  • Each UIView has a CALayer behind it to draw and display the content, and the size styles of UIView are provided by the Layer inside. Both have tree hierarchies, with SubLayers inside the layer and SubViews inside the View. But Layer has more AnchorPoint than View
  • When a View is displayed, UIView acts as a Layer’s CALayerDelegate, and the View is displayed by the internal CALayer’s display
  • CALayer is a default modification property to support implicit animation, so when you animate UIView’s Layer, the View acts as a proxy for the Layer, Layer requests the corresponding action from the View via actionForLayer:forKey
  • Layer internal maintenance of three layer tree, respectively presentLayer Tree(animation tree),modeLayer Tree(model tree), Render Tree(Render tree), when doing iOS animation, we modify the properties of animation, In the animation is actually the presentLayer property value of the Layer, and the final display on the interface is actually the modelLayer that provides the View
  • The most obvious difference between the two is that a View can accept and handle events, while a Layer cannot

7. Process and thread, parallel and concurrent, synchronous and asynchronous knowledge

A Process is a running activity of a program in a computer on a certain data set. It is the basic unit of the system for resource allocation and scheduling. Each Process is independent and runs in its dedicated and protected memory, which is the basis of the operating system structure.

A thread is the smallest unit in which an operating system can schedule operations. It is contained within the process and is the actual operating unit within the process. A thread is a single sequential flow of control in a process, and multiple threads can be concurrent in a process, each performing a different task in parallel.

The difference between threads and processes can be summarized as follows:

  • 1. A process is an executing program, the basic unit of resource allocation, and a thread is the basic unit of CPU scheduling.
  • 2, processes are independent of each other, and resources cannot be shared between processes. A process has at least one thread, and each thread of the same process shares the resources of the whole process (registers, stack, context).
  • 3. Thread creation and switching costs less than process.

Concurrency: The execution of two or more programs in the same time period, with temporal overlap (simultaneous macroscopically, sequential microscopically). In an operating system, concurrency refers to a period of time when several programs are running between start and finish, and all of these programs are running on the same processor, but only one program is running on the processor at any time.

Parallel: When the system has more than one CPU, the operation of the thread may be non-concurrent. When one CPU executes a thread, another CPU can execute another thread. The two threads do not occupy CPU resources, but can execute simultaneously. This method is called Parallel.

The difference between concurrency and parallelism:

Concurrency and parallelism are both similar and different concepts. Parallelism refers to two or more events occurring at the same time. Concurrency is when two or more events occur at the same time interval.

Sync: Only tasks can be executed on the current thread in sequence and new threads cannot be started. (Block the current thread and wait for the task to complete)

Asynchronous Async: Performs tasks in a new thread and has the ability to start a new thread. (Do not block the current thread, do not wait for the task to complete)

7. What are thread locks in iOS?

Locks used in iOS development, including Spinlock_t, OS_UNfair_LOCK, pthread_mutex_t, NSLock, NSRecursiveLock, NSCondition, NSConditionLock, @synchronized, dispatch_sema Phore, pthread_rwlock_t.

  • OSSpinLock: Not a thread-safe lock and may cause priority inversion
  • Os_unfair_lock: This is a low-level lock. Unlike OSSpinLock, threads waiting for os_UNFAIR_LOCK are hibernated (like run loop), not busy-wait.
  • Pthread_mutex_t: a cross-platform multi-thread mutex in C. The threads waiting for the lock are in hibernation state. You can initialize the pthread_mutex_t into different types of locks according to different attributes, such as mutex, recursive, and conditional locks.
  • NSLock: Based on mutex basic lock encapsulation, more object-oriented, the thread waiting for the lock will sleep. Inheriting from NSObject and following the NSLocking protocol, there are only two methods in the NSLocking protocol -(void)lock and -(void)unlock.
  • NSRecursiveLock: is a recursive lock. It differs from NSLock in that it can be repeatedly locked in the same thread without causing a deadlock. It is also based on mutex encapsulation and complies with NSLocking protocol.
  • NSCondition: the object actually acts as a lock and a thread inspector, and can decide whether to continue running the thread based on the condition. It is based on the encapsulation of the mutex base lock and conT condition, so it is a mutex and has its own condition, and the thread waiting for the lock sleeps
  • NSConditionLock is similar to NSLockin that it inherits NSObject and follows the NSLocking protocol and locks the try. However, the condition attribute is added
  • Synchronized is a mutex that supports multithreaded recursion.

Objc_sync_enter Key process analysis:

If no object cache is found in the cache list of the current thread, then check the listP total list structure. If object cache is found in the cache list of the current thread, Create a SyncData, set lockCount and threadCount to 1, and update the cache

Objc_sync_exit Key flow analysis:

Object If so, execute lockCount– threadCount– if the current lockCount==0, update the cache and end the process

  • “Dispatch_semaphore” is a GCD method of synchronization. There are only three functions related to it, one is to create a semaphore (dispatch_semaphore_create) and one is to wait for a semaphore (dispatch_semaphore_wait). One is dispatch_semaphore_signal.

The dispatch_semaphoRE_create (1) method creates a semaphore of type dispatch_semaphore_t with an initial value of 1. Note that the incoming argument must be greater than or equal to 0, otherwise dispatch_semaphore returns NULL

Synchronized related problem sets

  • How does the lock relate to the object you pass in @synchronized?

For every object you call sychronized, Objective-C Runtime assigns a recursive lock to it and stores it in a hash table.

  • Does @synchronized retain (add reference count) locked objects?

Using @synchronized does not increase the reference count for this object

  • What if the object passed to @synchronized is freed or assigned nil in the @synchronized block?

It looks OK if the inner object is released or set to nil in sychronized. This is not documented, so I won’t rely on it in production code.

  • What happens if @synchronized is passed an object value of nil?

@synchronized(nil) does nothing, the hash is empty, the lock fails, and the code block is not thread-safe. You can see if this happens by adding a breakpoint to objc_sync_nil.

8. Execution principle of dispatch_once

Dispatch_once ensures that tasks are executed only once, even if multiple threads are invoked simultaneously. Often used to create singletons, swizzeld method, and other functions.

if (dispatch_atomic_cmpxchg(vval, NULL, &dow)) { _dispatch_client_callout(ctxt, func); tmp = dispatch_atomic_xchg(vval, DISPATCH_ONCE_DONE); tail = &dow; while (tail ! = tmp) { while (! tmp->dow_next) { _dispatch_hardware_pause(); } sema = tmp->dow_sema; tmp = (struct _dispatch_once_waiter_s*)tmp->dow_next; _dispatch_thread_semaphore_signal(sema); } } else { dow.dow_sema = _dispatch_get_thread_semaphore(); for (;;) { tmp = *vval; if (tmp == DISPATCH_ONCE_DONE) { break; } dispatch_atomic_store_barrier(); if (dispatch_atomic_cmpxchg(vval, tmp, &dow)) { dow.dow_next = tmp; _dispatch_thread_semaphore_wait(dow.dow_sema); } } _dispatch_put_thread_semaphore(dow.dow_sema); }Copy the code

Dispatch_once executes the completion flag bit with the atomic operation block, uses the semaphore to ensure that only one thread executes the block, and wakes up all waiting threads after the block completes.

9. Implementation principle of dispatch_semaphore

Dispatch_semaphore is a common operation provided in GCD to ensure multithreaded security of resources and to control the number of concurrent tasks. Its essence is actually based on the Mach kernel semaphore interface to implement.

Dispatch_semaphore_t is a pointer to the dispatch_semaphore_s structure. First, take a look at the underlying data structure.

struct dispatch_queue_s; DISPATCH_CLASS_DECL(semaphore, OBJECT); struct dispatch_semaphore_s { DISPATCH_OBJECT_HEADER(semaphore); // Dsema_value and dsemA_ORIg are the semaphores that perform the tasks required by the semaphores. For a dispatch_semaphore_WAIT operation, dSEMA_value is subtracted once. long volatile dsema_value; long dsema_orig; _dispatch_sema4_t dsema_sema; };Copy the code

In the dispatch_SEMaphoRE_S structure: DSEMA_ORIg is the initial value of the semaphore, dsemA_value is the current value of the semaphore, and the semaphore API is operated by dsemA_value to realize its function.

  • Dispatch_semaphore_create Creates a new count semaphore with an initial long value.

Parameter value: The starting value of the semaphore. Passing a value less than zero results in NULL being returned. Return value result: Newly created semaphore, NULL on failure.

  • Dispatch_semaphore_wait Waits for (reduces) the semaphore.

Decrement the count semaphore. If the result value is less than zero, this function waits for the signal to appear and then returns. (You can decrease the total semaphore by 1, and wait until the total number of signals is less than zero, otherwise it will execute normally.)

  • Dispatch_semaphore_signal Sends (increases) signals. If the previous value is less than zero, this function wakes up the waiting thread before returning. If the thread is awakened, this function returns a non-zero value. Otherwise, zero is returned.

Semaphore_signal wakes up a thread waiting in semaphore_wait. If there are multiple waiting threads, wake up based on thread priority.

10. Implementation principle of Dispatch_group

Dispatch group is a synchronization mechanism based on semaphore, mainly provides the following five functions: enter, leave, wait, async, notify.

  • 1. Dispatch Group is a GCD feature that allows you to group tasks. When this set of tasks is complete, the caller is notified

Based on this, multiple concurrent tasks can be combined into a group so that the caller can know when they are all finished

  • Create dispatch group

dispatch_group_t dispatchGroup = dispatch_group_create();

  • 3. Two ways to group tasks:


void dipatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block); Dispatch_group_leave = dispatch_group_leave = dispatch_group_leave

void dispatch_group_enter(dispatch_group_t group); // Increment the number of tasks to be executed in the group

void dispatch_group_leave(dispatch_group_t group); // Decrease the number of tasks in a group

Dispatch_group_enter and dispatch_group_leave are equivalent to the reserve and release operations in reference counting and must be used together to prevent memory leaks

  • 4. Dispatch_group_wait: waits for the Dispatch group to complete and blocks the current thread

void dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); // The first argument is the group to wait for // the second argument is the wait time. Group 0 is returned if the execution time does not exceed timeout. A non-0 value is returned if the execution time exceeds timeout. Always wait with DISPATCH_TIME_FOREVER

  • 5. Dispatch_group_notify: Wait for the dispatch group to execute tasks in the block without blocking the current thread

void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block); // The first argument is the group to wait on. // The second argument is the queue in which the block is to be executed. // The third argument is the code block to wait on. Put the tasks that need to be processed after the group execution into the block

Dipatch_group_notify and dispatch_group_wait can wait for tasks in the group to complete before performing other operations. However, dispatch_group_notify blocks tasks that are about to be executed. Dispatch_group_wait blocks tasks that are about to be executed

  • 6. Dispatch_apply: used when a task needs to be executed repeatedly, blocking the current thread

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block) (size_t) ); “// Iterations” indicates the number of times to execute. // Queue You can use concurrent queues so that the system executes concurrently based on resources

11. Implementation principle of dispatch_barrier_Async

  • The dispatch_barrier_async function waits for all tasks appended to the parallel queue by the fence function to complete and then appends the task to the parallel queue by the fence function. Then, after the dispatch_barrier_async task is executed, the concurrent queue returns to normal action, and the task is appended to the queue behind the fence function and execution begins.
  • Use the fence function to use the self-created concurrent queue, not the system-provided global concurrent queue

Dispatch_barrier_sync and dispatch_barrier_async have something in common

1. Wait for tasks inserted in the queue before it to finish

2. Wait for them to finish their own task before performing the next one

Differences between dispatch_barrier_sync and dispatch_barrier_async

1. When dispatch_barrier_sync inserts its own tasks into the queue, it waits for its own tasks to finish before continuing to insert tasks written after it and then executing them

2. Dispatch_barrier_async does not wait for its own task to finish. It continues to queue subsequent tasks and waits for its own task to finish.

So the no-wait (asynchronous) nature of dispatch_barrier_Async is the process of inserting a task into a queue and its wait nature is the process of actually executing the task.

Fence to achieve multiple read single write

- (id)readDataForKey:(NSString *)key
    __block id result;
    dispatch_sync(_concurrentQueue, ^{
        result = [self valueForKey:key];
    return result;
- (void)writeData:(id)data forKey:(NSString *)key
    dispatch_barrier_async(_concurrentQueue, ^{
        [self setValue:data forKey:key];
Copy the code


The internal implementation logic of runloop

RunLoop with thread

  • Each thread has a unique RunLoop object corresponding to it
  • Runloops are stored in a global Dictionary, with threads as keys and runloops as values
  • The thread is created without a RunLoop object; the RunLoop is created the first time it gets it
  • The RunLoop is destroyed at the end of the thread
  • The RunLoop for the main thread is automatically acquired (created), and RunLoop is not enabled for the child thread by default

Difference between Source0 and Source1

• Source1: a system event, mach_port-based, from the kernel or other process or thread, that can actively wake up a dormant RunLoop. Just think of mach_port as a mechanism for sending messages between processes.

• Source0: application layer events, non-port-based processing events. This means that you did not receive this message directly from another process or kernel.

Relation between Timer and Runloop?

  • NSTimer is managed by RunLoop, NSTimer is actually CFRunLoopTimerRef, between them is toll-free bridged, can be exchanged;
  • If we use NSTimer on child threads, we must enable RunLoop on child threads, otherwise the timer will not work

Fixed timer failure on scroll view NSTimer added to both runloops

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

Problems with NSTimer and CADisplayLink

  • Not being on time: Both NSTime and CADisplayLink are underlying implementations of runloop-based CFRunLoopTimerRef, which means they rely on RunLoop. If runloops are too busy, they may not be on time

Solution: Use GCD timer. The GCD timer is tied directly to the kernel and does not rely on RunLoop, so it is very punctual

A circular reference

1, Apple system API can solve (iOS10 above)

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats: (BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats: (BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)); - (instancetype)initWithFireDate:(NSDate *)date interval: (NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), Ios (10.0), watchos (3.0), tvos (10.0));Copy the code

2. Use block to solve the problem

#import "NSTimer+PFSafeTimer.h"

@implementation NSTimer (PFSafeTimer)

+ (NSTimer *)PF_ScheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval block:(void(^)(void))block repeats:(BOOL)repeats {
    return [NSTimer scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(handle:) userInfo:[block copy] repeats:repeats];

+ (void)handle:(NSTimer *)timer {
    void(^block)(void) = timer.userInfo;
    if (block) {
Copy the code

3. Use NSProxy to solve circular references

#import "PFProxy.h"

@interface PFProxy()

@property (nonatomic, weak) id object;

@implementation PFProxy

- (instancetype)initWithObjc:(id)object {
    self.object = object;
    return self;

+ (instancetype)proxyWithObjc:(id)object {
    return [[self alloc] initWithObjc:object];

- (void)forwardInvocation:(NSInvocation *)invocation {
    if ([self.object respondsToSelector:invocation.selector]) {
        [invocation invokeWithTarget:self.object];

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.object methodSignatureForSelector:sel];
Copy the code

Runloop is related to events

  • Event response process

IOKit. Framework generates an IOHIDEvent event when a hardware event (touch, lock screen, rotation, etc.) occurs. Events are received by The SpringBoard and distributed to the App process via mach_port, which then wraps IOHIDEvent into UIEvent events for processing and distribution via the registered source1 callback.

  • Gesture recognition process

Once a gesture is recognized in the source1 callback, cancel is called to interrupt the touchBegin/Move/End series of events, and the recognized gesture is marked as pending. Apple registers an Observer to listen for beforeWaiting events, a callback in which the pending gesture event is picked up and processed accordingly

Runloop rendering

  • Runloop Rendering process

When [UIView setNeedsDisplay] is called, the setNeedsDisplay method of UIView layer is called back, which is equivalent to marking the layer. Instead of drawing directly, it does so until the beforeWaiting of the current runloop. Call [CALayer display] to draw. Implement the layer.delegate method displayer:, which is the interface for asynchronous drawing; If not, the system drawing process is completed.

  • System drawing process

Create a BackingStore to get the graphics context and determine if there is a Delegate. If yes, call [layer.delegate drawLayer:inContext:] and return the [UIView draw] callback to do something else based on the system draw; If no, call [CALayer drawInContext:]. The above two branches will store the drawing in BackingStore, and then submit it to GPU, and the drawing is finished.

  • Asynchronous rendering

Use child threads in the asynchronous draw entry ([layer.delegate displayer:] mentioned above) to draw the desired content and assign the layer.contents property via bitmap

Runloop in AFNetworking2.0

AFNetworking adds an NSMachPort before runloop starts so that runloop does not exit. The code implementation is as follows


Principle: autoreleasePoolPage is a bidirectional linked list (an autoreleasePoolPage is equivalent to a node). App starts, apple will register two observer, the callback is _wrapRunloopWithAutoreleasePoolHandler ().

  • A, The first observer listens for the Entry event, and its callback will call _objc_autoreleasePoolPush() to create the release pool. It has the highest priority and is guaranteed to be created first.

  • B, The second Observer monitors two events:

    1) beforeWaiting events: In the beforeWaiting event callback, _objc_autoreleasePoolPop() and _objc_autoreleasePoolPush() are called torelease the old release pool and create a new release pool;

    2) Exit event: In the exit event callback, _objc_autoreleasePoolPop() is called torelease the new release pool, which has the lowest priority and is guaranteed to execute last

The basic structure of autoReleasePoolPage is shown below (size: 4096bytes= 4K), AutoreleasePoolPage member variables are inherited from AutoreleasePoolPageDate, they require a total of 56 bytes of space, then the remaining 4040 bytes of space, an object pointer takes up 8 bytes, An AutoreleasePoolPage can hold 505 objects that need to be released automatically.

Runloop and caton monitoring

Monitor the main run loop from kCFRunLoopBeforeSources (or kCFRunLoopBeforeTimers) to kCFRunLoopAfterWaiting If the time taken by the activity change of the value exceeds our predetermined threshold, then we can judge whether there is a lag. When there is a lag, we can immediately read the current function call stack to help us analyze the code problem.

13. The Dealloc process explains the implementation principle of Dealloc

Inline void objc_object::rootDealloc() {if (isTaggedPointer()) return; inline void objc_object::rootDealloc() {if (isTaggedPointer()) return; // fixme necessary? If (fastPath (isa.nonpointer && // if the object uses an optimized ISA counting method! Isa.weakly_referenced && // The object is not weakly referenced! Isa.has_assoc && // Object has no associated object! Isa.has_cxx_dtor && / objects have no custom C++ destructors! Isa.has_sidetable_rc // The object does not use sideTable for reference counting)) { sidetable_present()); free(this); Object_dispose ((id)this); // Dispose object_dispose((id)this); }}Copy the code

The entire Dealloc method release process is shown below:


  • The object_cxxDestruct function is executed
  • Execute _object_remove_assocations to remove the associated objects (this is why removing is not necessary when a category adds attributes).
  • Empty the reference count table and clear the weak reference table, setting the weak pointer to nil

What are the 64 bits in ISA_BITFIELD

Uintptr_t nonpointer: 1; Uintptr_t has_assoc: 1; // If not, the object can be destroyed faster. // Call object_cxxDestruct to uintptr_t has_cxx_dtor before destructing the object. 1; // uintptr_t shiftcls: 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ / Weak_clear_no_lock is called before the object is destroyed. Weak_clear_no_lock sets the weak reference of the object to nil. // And call weak_entry_remove to remove the entry of object from weak_table uintptr_t Weakly_referenced: 1; // Mark whether the object is destroying the Uintptr_t dealLocating: 1; Uintptr_t has_sidetable_rc: 1 uintptr_t has_sidetable_rc: 1; Uintptr_t extra_rc: // Save the value of this object's reference count -1 (store RC_HALF after overflow) 2^ 8-1 = 255 # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18)Copy the code

The AssociatedObject principle uses objc_setAssociatedObject and objc_getAssociatedObject to simulate the access methods of properties, respectively, and uses Associated objects to simulate instance variables

/** * @param object The source object to associate with * @param Key The associated key * @param value The value associated with the key of the source object. Pass nil to clear an existing association. * @param policy Association policy * * @see objc_setAssociatedObject * @see objc_removeAssociatedObjects */ OBJC_EXPORT void objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id _Nullable value, objc_AssociationPolicy policy); /** * @param object * @param key The associated key * @return The value associated with The key \e key for \e object * @see objc_setAssociatedObject */ OBJC_EXPORT id _Nullable objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key);Copy the code

The data structure used by the Associated Object can be summarized as follows:

  • Get a global AssociationsHashMap using the Get function of the AssociationsManager.
  • According to our source object DisguisedPtr

    get the ObjectAssociationMap from AssociationsHashMap.
  • Get the Object Society from the ObjectAssociationMap based on the association key we specified (const void *key).
  • The two member variables of ObjcAssociation hold our association policy _policy and association value _value, respectively.


1. Realization principle of Category

After Category compilation, the underlying structure is struct category_t, which stores the classified object method, class method, attribute and protocol information. When the program runs, the Runtime will merge the Category data into the class information (class object, metaclass object).

2. What is the difference between Category and Class Extension?

When Class Extension is compiled, its data is already contained in the Class information, and when Category is run, the data is merged into the Class information

What is the difference between load and initialize methods?

1. Call method

1> Load is called from the function address. 2> Initialize is called from objc_msgSend

2. Call time

Load is called when the Runtime loads a class or class (only once). Load is called when the Runtime loads a class or class (only once). Initialize is called when the class first receives a message.

4. Load and initialize call sequence


B) The parent class load is called first before the subclass load is called

A) If the class is compiled first, load will be called first


1> Initialize the parent class. 2> Initialize the child class.

5, how to implement the classification “add member variable”?

By default, member variables cannot be added to a category because of the underlying structure of the category. But it can be done indirectly by associating objects

Void objc_setAssociatedObject(id object, const void * key, ID value, Objc_getAssociatedObject (ID object, Const void * key) Remove all associated objects void objc_removeAssociatedObjects(id object)Copy the code


1. How does block work? What is the essence?

A block is essentially an OC object that also has an ISA pointer inside it. A block is an OC object that encapsulates a function call and the environment in which that function is called

2, Block (capture)

To ensure that external variables can be accessed within a block, blocks have a variable capture mechanism

3. What are the types of blocks

There are three types of blocks, which you can see by calling the class method or isa pointer. They are all derived from the NSBlock type

Nsconcreteglobalblock (_NSConcreteStackBlock); NSStackBlock (_NSConcreteStackBlock); _NSConcreteMallocBlock)

4, Block copy

In an ARC environment, the compiler automatically copies blocks on the stack to the heap as needed, such as the following

  • 1. Block returns as a function
  • 2. Assign block to the __strong pointer
  • 3. Block is used as a Cocoa API method name containing the usingBlock method argument
  • 4. Block as method parameter of GCD API
@property (copy, nonatomic) void (^block)(void); @property (strong, nonatomic) void (^block)(void); @property (copy, nonatomic) void (^block)(void);Copy the code

5. The __block modifier

__block can be used to solve the problem of not being able to modify the value of the auto variable inside a block

__block cannot modify global or static variables.

The compiler wraps a __block variable into an object

When a __block variable is on the stack, there is no strong reference to the object to which it points

When a __block variable is copied to the heap

The copy function inside the __block variable calls the _Block_object_assign function The _Block_object_assign function provides retained or weak references based on the __strong, __weak, and __unsafe_unretained modifiers. This is limited to retain in ARC and not retain in MRC.)

If the __block variable is removed from the heap

The dispose function inside the __block variable is called. The dispose function inside the __block variable is called. The _Block_object_dispose function is called.

6. Circular references

Use __weak and __unsafe_unretained

__unsafe_unretained typeof(self) weakSelf = self; self.block = ^{ print(@"%p", weakSelf); } copy code __weak Typeof (self) weakSelf = self; self.block = ^{ print(@"%p", weakSelf); __block id weakSelf = self; weakSelf = self; weakSelf = self; self.block = ^{ weakSelf = nil; } self.block();Copy the code

17. Implementation principle of weak pointer

The Runtime maintains a weak table that stores all the weak Pointers to an object. The weak table is a hash table where key is the address of the pointer to the weak object and Value is the address of the pointer to the weak object

  • 1. Initialization: The Runtime calls the objc_initWeak function and initializes a new weak pointer to the address of the object
  • 2. When adding a reference: the objc_initWeak function will call storeWeak(), which updates the pointer pointer and creates the corresponding weak reference table
  • 3. When releasing, call the clearDeallocating function. The clearDeallocating function first fetches an array of weak pointer addresses based on the object’s address, then iterates through the array to set it to nil, deletes the entry from the Weak table, and clears the object’s record

18. Compare with OC. What are the advantages of Swift

  • Swift is more secure, it’s a type-safe language.
  • Swift is easy to read, with simple syntax and file structure.
  • Swift is easier to maintain and has a clearer structure when files are separated.
  • Swift has less code and a concise syntax that saves a lot of redundant code
  • Swift is faster and performs better.

Why is Swift slow to compile?

Because Swift does a lot of work at compile time, it’s normal that it takes a bit more time. Such as the type of analysis.

Why is Swift faster than OC?

Global optimization of the Whole Module Optimizations mechanism of the compiler, more stack memory allocation, less reference count, more statics, use of protocol types are all reasons why Swift is faster than OC.

Delegate, Notification, and KVO

  • Delegate. One-on-one
  • Notification one-to-many, many-to-many
  • KVO one-to-one

The three have their own characteristics:

  • The delegate syntax is concise, easy to read, and easy to debug
  • Notification is flexible and can be used across multiple classes
  • KVO implements attribute listening, model and View synchronization
  • You can use different approaches depending on the scenarios you encounter in your actual development

Notification is different from KVO

  • KVO provides a mechanism to automatically notify responding observers when the properties of a specified object being observed are modified. KVC(key-value coding) is the basis of KVO
  • Notice: is a broadcast mechanism, in practice, through the notification center object, an object can be for all concerned about the object of this time sending messages, both are the observer pattern, different is the KVO is observed directly send a message to the observer, is direct interaction between objects, notice is the center for the both and notification object interaction, between objects The tao each other
  • Kvo is based on Runtime and has a notification center for notifications

Notification is different from delegate

  • The essential difference between a Delegate and Notification is imperative and responsive
  • Delegate one to one, Notification one to many

20.NSCache & NSDictionary & NSURLCache

  • Hash table (NSDictionary uses hash tables to map and store keys and values)
  • NSURLCache provides a comprehensive in-memory and on-disk caching mechanism for YOUR application’s URL requests. As part of the base library URL loading system, any request loaded through NSURLConnection will be handled by NSURLCache. (NSCache has nothing to do with NSURLCache)


  • The NSCache and NSMutableDictionary functions are basically the same.

The difference between:

  • NSCache is thread-safe, NSMutableDictionary threads are not
  • NSCache threads are safe, and classes developed by Mutable are generally thread-unsafe
  • NSCache automatically frees memory when it runs out of memory (so always check for empty data when fetching from the cache)
  • NSCache can specify the cache quota. If the cache quota is exceeded, the memory is automatically released
  • NSCache does not “copy” the key, but rather “preserve” it. Therefore, NSCache does not automatically copy keys, so this class is more convenient than a dictionary in cases where keys do not support copying operations.
Cache limit: number of caches @property NSUInteger countLimit; @property NSUInteger totalCostLimit; Apple has packaged NSCache with more methods and properties than NSMutableDictionary doesCopy the code

Functional programming & Chain programming & Responsive programming

  • Functional programming is a programming model that treats computer operations as functions in mathematics and avoids the concepts of states and variables.
  • Chain programming is the sequential writing of code that needs to be executed. It makes the code easy to understand.
  • Responsive programming is a programming paradigm for data flow and change propagation.

22. The difference between Block and Protocol. What problem is a Block used to solve?

  • The common feature of proxy and block is the callback mechanism, but the difference is that the proxy method is more, more scattered, common interface, more methods and choose to use delegate to decouple, the code using block is more centralized and unified, asynchronous and simple call back block is better
  • Blocks are created for scheduling between multiple threads;
  • Block is also an OC object that can be passed as an argument, which is easy to use, simple, flexible, and requires very little code to implement code callbacks. There is a lot of code to negotiate

23. Principle of thread pool

  • If the thread pool size is smaller than the core thread pool size
    1. Create threads to execute tasks
  • If the thread pool size is greater than or equal to the core thread pool size
    1. Determine whether the thread pool work queue is full
    2. If not, the task is pushed to the queue
    3. If it is full and maximumPoolSize>corePoolSize, a new thread will be created to execute the task
    4. Otherwise, leave it to the saturation strategy
    Parameter names On behalf of the meaning
    corePoolSize Base size of the thread pool (core thread pool size)
    maximumPool The maximum size of the thread pool
    keepAliveTime The maximum lifetime of idle threads in the thread pool that exceed the corePoolSize tree
    unit KeepAliveTime Indicates the time unit of the parameter
    workQueue Task blocking queue
    threadFactory Create a factory for the thread
    handler If the number of submitted tasks exceeds the sum of maxmumPoolSize and workQueue, the task will be processed by RejectedExecutionHandler

There are four saturation strategies as follows:

  • AbortPolicy directly thrown RejectedExecutionExeception exceptions to prevent normal operation of system
  • CallerRunsPolicy rolls back the task to the caller
  • DisOldestPolicy Discards the most awaited task
  • DisCardPolicy Discards the task directly

24. Atomic implementation mechanism; Why can’t we guarantee absolute thread safety

  • Atomic is the thread that uses spinlock_t in setter and getter methods to keep them safe.
@property (nonatomic, assign) NSInteger obj; - (void)viewDidLoad { [super viewDidLoad]; NSLock *m_lock = [NSLock new]; // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ^{for (int I = 0; i < 10000; i ++){ [m_lock lock]; self.obj = self.obj + 1; [m_lock unlock]; } NSLog(@"obj: %ld thread: %@",(long)self.obj, [NSThread currentThread]); }); // dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ^{for (int I = 0; i < 10000; i ++){ [m_lock lock]; self.obj = self.obj + 1; [m_lock unlock]; } NSLog(@"obj: %ld thread: %@",(long)self.obj, [NSThread currentThread]); }); } // Output: 2020-04-15 16:19:54.566420+0800 Atomic2Nonatomic[31970:4554604] obj: 15712 thread: <NSThread: 0x600000F22880 >{number = 6, name = (null)} 2020-04-15 16:19:54.566542+0800 Atomic2Nonatomic[31970:4554603] obj: {number = 6, name = (null)} 2020-04-15 16:19:54.566542+0800 Atomic2Nonatomic[31970:4554603] obj: <NSThread: 0x600000F1e040 >{number = 4, name = (null)} This part is not thread-safe, and this +1 operation is not thread-safe, so to get 20000, you need to lock self.obj = self.obj +1. The code will get what we want.Copy the code
  • Atomic does not guarantee that threads are absolutely safe


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.

26. What are the advantages of NSOperation compared with GCD?

GCD is a low-level API based on C, NSOperation belongs to object-C class. NSOperation was first introduced in iOS, then GCD and NSOperationQueue were introduced in iOS 4 and internally implemented in GCD. Relative to GCD:

  • NSOperation has more functions available. See the API for details.
  • 2. In NSOperationQueue, dependencies between nsOperations can be established.
  • 3. Kvo monitors whether an operation isExecuted, finished, or cancelled.
  • 4. NSOperationQueue can easily manage priorities between concurrent and NSOperations. GCD is mainly used in conjunction with blocks. The code is simple and efficient. GCD can also implement complex multi-threaded applications, mainly to establish the time dependence of each thread such as the situation, but the need to implement itself is more complex than NSOperation

27.TCP triple handshake and quadruple wave

TCP three-way handshake

First handshake: The client sends a SYN packet to the server with the SYN Flag set to 1, and the ISN (Initial Sequence Number) of the packet is the ISN (Initial Sequence Number) of the client. After the packet is sent, the client enters the SYN_SEND state, and the client knows that the client can send packets properly.

Second handshake: After receiving the connection request from the client, the server responds with a SYN+ACK packet with the SYN flag at position 1 and ACK flag at position 1. The ISN of the server is different from that of the client, and the ISN of the client is displayed as the ISN plus 1. After the packet is sent, the server enters the SYN_RCVD state. The server knows that the packet sending capability of the client is normal, the packet sending capability of the server is normal, and the packet receiving capability of the server is normal.

Third handshake: After receiving the response from the server, the client responds with an ACK packet. The ACK of the packet is set to 1, the ISN+1 of the client is displayed, and the ISN+1 of the server is displayed. After the packet is sent, the client changes to ESTABLISHED, and the client knows that the server can send packets properly. The packet receiving capability of the server and client is normal. After receiving the packet, the server enters the ESTABLISHED state. The server knows that the packet receiving capability of the client is normal. At this point, the TCP connection is successfully ESTABLISHED and data can be transmitted.

The three-way handshake ensures that both the client and server know that the receiving and sending capabilities of the other side are normal. An error is generated to prevent the server from opening some useless connections and increasing server overhead, and to prevent the invalid connection request message segment from suddenly being sent to the server.

TCP waved four times

First wave: The active party sends a FIN packet with the sequence number U (equal to the sequence number of the last byte of the sent data plus 1) and the ISN+1 to the passive. The active party enters the FIN_WAIT_1 state and waits for the confirmation from the passive party.

Second wave: Passive send an ACK packet to the initiative, confirm the active party can close the connection, the serial number in this message is v (determined by the data sequence of already issued), the code for u + 1, an ACK packet into passive CLOSE_WAIT state, check whether itself and data is sent to the other side – passive side is closed at this time, That is, the active party has no data to send, but if the passive party sends data, the active party still needs to receive it. This state will continue for some time, which is the duration of the CLOSE_WAIT state. After receiving the ACK message, the active party changes to the FIN_WAIT_2 state and waits for the FIN packet from the passive party.

Third wave: again, the passive party checks that it has no data to send to the active party, and before that, the passive party is likely to send some data to the active party, assuming that the serial number is W. The passive sends a FIN+ACK packet with the sequence number W and confirmation code U +1 to the active party. After sending the FIN+ACK packet, the passive party changes to the LAST_ACK state and waits for the active party to confirm the connection disconnection.

Fourth wave: The active party receives the FIN packet from the passive party and responds with an ACK packet with the sequence number U +1 and verification code W +1. After being sent, the active party switches to TIME_WAIT state, and disconnects after 2∗MSL (Maximum Segment Lifetime), and then enters the available state CLOSED. After receiving the last ACK packet, the passive party disconnects and enters the CLOSED state.

TCP is a connection-oriented, reliable, byte stream – based transport layer communication protocol in full duplex mode. You can’t disconnect until you’ve waved four times, because there’s probably still data that hasn’t been sent. The TCP connection can be disconnected and reliable only after the two parties ACK FIN packets of each other.

27. XML parsing for iOS data parsing

There are two common ways of parsing XML :DOM parsing and SAX parsing

The DOM parsing

DOM:Document Object Model. When parsing XML, you read the entire XML document and build a memory-resident tree structure (tree of nodes) that allows you to retrieve any XML node by traversing the number structure, read its attributes and values, and, often with XPath, query the XML node directly. DOM parsing of data requires the use of a third-party class called GDataXMLNode

SAX parsing

SAX:Simple API for XML, based on event-driven parsing, parsing data line by line (using protocol callback mechanism).nsXMLParser

28. What are the persistence methods in iOS?

  • Property list file — The store of NSUserDefaults actually generates a plist file locally and stores the desired properties in the plist file
  • Object archiving – Files are created locally and data is written to any file type
  • SQLite Database – Create database files locally for data processing
  • CoreData – Same idea as database processing, but implemented differently

29. Deep copy, shallow copy

  • Deep copy: same contents, new memory space, new Pointers
  • Shallow copy: Copies Pointers
  • Copy of immutable objects is a shallow copy and mutableCopy is a deep copy
  • Both copy and mutableCopy of a mutable object are deep copies

To be continued…