Introduction: This article is the summary of my teacher Li Mingjie’s iOS basic principle class (below) /OC object/associated object/multithreading/memory management/performance optimization, which lasted intermittently for about 3 months. I made notes of what I listened to in class.

  • 1. How much memory an NSObject takes up
  • 2. Classification of OC objects
  • 3. Implementation principle of KVO
  • 4. Implementation principle of KVC
  • 5, classification,
    • 5.1 Realization principle of classification
    • 5.2 Implementation principles of Load and Initialize
  • 6, Block underlying decryption
  • 7. Implementation principle of RunLoop
  • 8. Implementation principle of RunTime
    • 8.1 ISA resolution
    • 8.2. Method caching
    • 8.3. Objc_msgSend Execution process
    • 8.4. @dynamic keyword
    • 8.5 the difference between Class and SuperClass
    • 8.6 difference between isKindOfClass and isMemberOfClass
    • 8.7. RunTime apis
  • 9. Multi-threading
    • 9.1. Multi-threaded interview questions
    • 9.2 NSThread for multi-threading
    • 9.3 multi-threaded GCD
    • 9.4 multithreading NSOperation
    • 9.5. Thread safety of multithreading
    • 9.6, the dead lock
    • 9.7. Advanced usage of GCD
    • 9.8 communication between threads
  • 10. Memory management
    • 10.1 Timer Target memory leaks
    • 10.2, Tagged Pointer
    • 10.3 Principle of copy&retain& Strong
    • 10.4. Weak&assign principle
    • 10.5. What is the nature of @ Property
    • 10.6. Principle of AutoRelease
    • 10.7. Are Atomic thread-safe

Summary is not easy, time consuming, your little star ✨ is my infinite power. The originaladdress


We often look at some interview questions, but many interview questions we do not know why, if you carefully read the dozens of articles I summarized above, then you will also know why.

OC Object nature

1. How much memory does an NSObject take up?

The system allocates 16 bytes to NSObject (obtained by the malloc_size function), but only 8 bytes of space are used inside NSObject (available by class_getInstanceSize in 64bit environments).

Where is the isa pointer to the object?

  • The ISA of an instance object points to a class object
  • The ISA of a class object points to a meta-class object
  • The ISA of a meta-class object points to a meta-class object of the base class

3. Where is the OC class information stored?

  • Object methods, properties, member variables, and protocol information are stored in class objects
  • Class method, stored in a meta-class object
  • The specific value of the member variable, stored in the instance object

For details, see: 1. How much memory an NSObject object occupies. 2

KVO

1. How does iOS implement KVO for an object? (What is the nature of KVO?)

  • Use the RuntimeAPI to dynamically generate a subclass and have the Instance object’s ISA point to the new subclass
  • Foundation is called when the properties of an Instance object are modified_NSSetXXXValueAndNotifyfunction
    • 1, callwillChangeValueForKeymethods
    • 2, callsetAgemethods
    • 3, calldidChangeValueForKeymethods
    • 4,didChangeValueForKeyOberser’s method is called internallyobserveValueForKeyPath:ofObject:change:context:methods

2. How to trigger KVO manually?

Manually call willChangeValueForKey and didChangeValueForKey:

3. Does directly modifying a member variable trigger KVO?

KVO will not trigger

For specific implementation, please refer to: 3. Implementation principle of KVO

KVC

1. Will modifying attributes via KVC trigger KVO?

KVO is triggered because KVC calls set and KVO listens for set

2. What is the assignment and value process of KVC? How does it work?

KVO's setValue:forKey principle

  • 1. Search for member methods in the order setKey, _setKey. If a method is found, pass the argument and call the method
  • 2, if not found, check the accessInstanceVariablesDirectly return values (accessInstanceVariablesDirectly the return value of the default is YES).
    • The return value to YES, according to the _Key, _isKey, Key, isKey sequential search member variables, if found, direct assignment, if not found, call setValue: forUndefinedKey:, throw an exception
    • Return NO, direct call setValue: forUndefinedKey:, throw an exception

ValueforKey principle of KVO

  • 1. Search for member methods in the order of getKey,key,isKey, and _key
  • 2, if not found, check the accessInstanceVariablesDirectly return values
    • The return value to YES, according to the _Key, _isKey, Key, isKey sequential search member variables, if found, the value directly, if not found, call setValue: forUndefinedKey:, throw an exception
    • Return NO, direct call setValue: forUndefinedKey:, throw an exception

For specific implementation, please refer to: 4. Implementation principle of KVC

Category

1. Realization principle of Category

  • After Category compilation, the underlying structure is struct category_t, which stores classified object methods, class methods, attributes, and protocol information
  • At runtime, the Runtime merges 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 included in the Class information
  • Category data is merged into the class information at runtime

What is the difference between load and initialize methods?

  • 1. Call method

    • 1> Load is called directly based on the function address
    • 2> Initialize is called with objc_msgSend
  • 2. Call time

    • 1> Load is called when the Runtime loads a class or class (only once)
    • 2> Initialize is called when a class receives a message for the first time. Each class is initialized only once (the parent class’s initialize method may be called multiple times).

4. Load and initialize call sequence

1.load

  • 1> Call the load class first
    • A) Call load first if the class is compiled first
    • B) The load of the subclass is called before the load of the parent class is called
  • 2> Call the load of the class again
    • A) Call load first if the class is compiled first

2.initialize

  • 1> Initialize the parent class first
  • 2> Initialize the subclass (it may end up calling the initialize method of the parent 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

For details, see 5.1 Implementation principle of classification 5.2 Implementation principle of Load and Initialize

Block

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

  • A block is also essentially an OC object with an ISA pointer inside it
  • A block is an OC object that encapsulates a function call and its environment

2, Block (capture)

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

There are three types of blocks. You can check the types by calling the class method or isa pointer. All of them are derived from NSBlock types

  • Nsconcreteglobalblock (_NSConcreteGlobalBlock
  • 2, NSStackBlock (_NSConcreteStackBlock)
  • 3, NSMallocBlock (_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 is called
    • Copy calls the _Block_object_assign function internally
    • 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

    • Dispose function inside the __block variable is called
    • The _Block_object_dispose function is called internally
    • The _Block_object_dispose function automatically releases the object it points to.

6. Circular references

  • Use __weak and __unsafe_unretained
__unsafe_unretained typeof(self) weakSelf = self;
self.block = ^{
 print(@"%p", weakSelf);
}
Copy the code
__weak typeof(self) weakSelf = self;
self.block = ^{
 print(@"%p", weakSelf);
}
Copy the code
  • Solve with __block (block must be called)
__block id weakSelf = self;
self.block = ^{
weakSelf = nil;
}
self.block();
Copy the code

For details, see: 6. Block underlying decryption

RunTime

1. Talk about the message mechanism of OC

  • Method calls in OC are actually converted into calls to objc_msgSend, which sends a message to the receiver (selector method name).
  • Objc_msgSend has three underlying phases: message sending (current class, lookup in parent class), dynamic method parsing, and message forwarding

2. Process of message forwarding mechanism

  • 1. Message sending
  • 2. Dynamic method analysis
  • 3. Message forwarding

Message sending phase

The message sending process is the one we use most often, and others like dynamic method resolution and message forwarding are actually remedies. The specific process is as follows

  • 1. Firstly, determine whether the message receiver is nil. If it is nil, exit message sending directly
  • 2. If there is a receiverClass, search for the method in the cache of the receiverClass. If the method is found, invoke it directly. If you can’t find it, proceed
  • If no method is found in the receiverClass cache, search for the method from the receiverClass class_rw_t. If the method is found, execute the method and cache it to the receiverClass cache. If not, proceed
  • If no method is found in the receiverClass file, use the superClass pointer to find the superClass file. If the method is found, execute the method and cache it to the receiverClass cache. If not, proceed
  • If no method is found in the superClass cache, search for the method from the superClass class_rw_t. If the method is found, execute the method and cache it to the receiverClass cache. If no, repeat steps 4 and 5. If you can’t find the superClass, proceed
  • 6. If the method cannot be found in the low-level superClass, go to dynamic method resolution

Dynamic method parsing

Developers can implement the following methods to dynamically add method implementations

  • +resolveInstanceMethod:
  • +resolveClassMethod:After dynamic parsing, the system resumes the message sending process and searches for methods from the receiverClass cache

forward

If a method does not find related methods during the message sending phase and does not perform dynamic method resolution, the message forwarding phase is reached.

  • callforwardingTargetForSelector, is called if the return value is not nilobjc_msgSend(Return value, SEL)
  • callmethodSignatureForSelectorWithout returning nil, call the forwardInvocation: method; Called when the return value is nildoesNotRecognizeSelector:methods
  • Developers can use theforwardInvocation:Custom any logic in the method
  • The above methods have two versions of object method, class method (can be preceded by a plus sign +, can also be a minus sign -)

3. What is Runtime? Have you used it in your daily projects?

  • OC is a very dynamic programming language, allowing many operations to be deferred until the program is running
  • The dynamism of OC is supported and implemented by Runtime, which is a SET of C LANGUAGE APIS that encapsulates many functions related to dynamism
  • The OC code written at ordinary times is converted into the Runtime API to call

The specific application

  • Add attributes to categories using AssociatedObject
  • Iterate over all member variables of the class (changing placeholder text color of TextField, dictionary conversion model, auto archive unfile)
  • Switching method implementation (switching system method)
  • Use the message forwarding mechanism to resolve exceptions where methods cannot be found

4. The nature of super

  • The super call, which is converted to the objc_msgSendSuper2 function, takes two arguments
    • struct objc_super2
    • SEL
  • Receiver is the message receiver
  • Current_class is the Class object of the receiver

Please refer to:

  • 8.1 ISA resolution
  • 8.2. Method caching
  • 8.3. Objc_msgSend Execution process
  • 8.4. @dynamic keyword
  • 8.5 the difference between Class and SuperClass
  • 8.6 difference between isKindOfClass and isMemberOfClass
  • 8.7. RunTime apis

RunLoop

1. Talk about RunLoop. Is it useful in the project? 2. In the chat interface, we need to continuously save chat information into the database. At this time, we need to start a keepalive thread and process it in this thread

2, Runloop internal implementation logic

Each time a RunLoop is run, the thread’s RunLoop automatically processes the previously unprocessed message and notifies relevant observers. Specific order

  • Observers are notified that RunLoop is about to start
  • Notifying observers of any upcoming timers
  • Observers are notified that source0 events are about to be handled
  • 4. Process source0
  • 5. If there is source1, skip to step 9
  • 6. Notify observers that threads are about to enter hibernation
  • Put the thread to sleep until any of the following events occur
    • 1. The source0 event is triggered
    • 2. Start the timer
    • 3. External manual wake up
  • Notifying observers that threads are about to wake up
  • 9. Process the time received on wake up, then jump back to 2
    • 1. If the user-defined timer is started, process the timer event
    • 2. If source0 is started, pass the corresponding message
  • 10. Notify the observer of the end of RunLoop

Runloops and threads

  • 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

4. Relation between Timer and Runloop?

  • 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, you can only exit the Loop and select another Mode to enter
  • Different groups of Source0 / Source1 / Timer/Observer can be separated, each other
  • If there is no any Source0 / Source1 / Timer Mode/Observer, RunLoop immediately exit

Fixed timer failure on scroll view NSTimer added to both runloops

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

RunLoop has several states

KCFRunLoopEntry = (1UL << 0), // About to enter RunLoop kCFRunLoopBeforeTimers = (1UL << 1), KCFRunLoopBeforeSources = (1UL << 2), Source kCFRunLoopBeforeWaiting = (1UL << 5), KCFRunLoopAfterWaiting = (1UL << 6), kCFRunLoopExit = (1UL << 7), and RunLoop is about to exitCopy the code

**6, RunLoop mode function **

The system registers 5 modes

KCFRunLoopDefaultMode // the default Mode of App, usually the main thread is run in this Mode UITrackingRunLoopMode // interface tracking Mode, ScrollView tracking touch sliding, Ensure the interface slip is not affected by other Mode UIInitializationRunLoopMode / / when just start the App the first to enter the first Mode, Start after the completion of the will no longer use GSEventReceiveRunLoopMode / / acceptance system internal Mode of events, usually in less than kCFRunLoopCommonModes / / this is a place-holder Mode, is not a real ModeCopy the code

But we can only use two modes

KCFRunLoopDefaultMode // the default Mode of App, usually the main thread is run in this Mode UITrackingRunLoopMode // interface tracking Mode, ScrollView tracking touch sliding, Ensure that the interface is not affected by other modes when slidingCopy the code

For details, see: 7. Implementation principle of RunLoop

multithreading

1. Do you understand multi-threading? 2. What are the multithreading schemes for iOS? Which one do you prefer? 3. Have you ever used GCD in a project? What is the difference between OperationQueue and GCD and what are the advantages of each? Using thread locks

  • 1, OSSpinLock
  • 2, os_unfair_lock
  • 3, the pthread_mutex
  • 4, dispatch_semaphore
  • 5, dispatch_queue (DISPATCH_QUEUE_SERIAL)
  • 6, NSLock
  • 7, NSRecursiveLock
  • 8 NSCondition.
  • 9 NSConditionLock.
  • 10, @ synchronized
  • 11, pthread_rwlock
  • 12, dispatch_barrier_async
  • 13, atomic

7, thread communication embodiment of communication between threads

  • 1. One thread passes data to another thread
  • 2. After a particular task is executed in one thread, the task is continued in another thread

Nsthreads can register their current thread objects with a global object, so that they can obtain each other’s thread objects, and then use the following methods to communicate with each other. Since the main thread is special, the framework directly provides methods to execute on the main thread

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
 
Copy the code

2, the GCD

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
      
 });
Copy the code

Memory management

1. What are the precautions when using CADisplayLink and NSTimer? CADisplayLink and NSTimer make strong references to target, and if target makes strong references to them, circular references are raised

2. Introduce several large areas of memory

  • Code snippet: compiled code
  • Data segment
    • String constants: NSString * STR = @”123″
    • Initialized data: initialized global variables, static variables, etc
    • Uninitialized data: uninitialized global variables, static variables, etc
  • Stack: Function call overhead, such as local variables. The allocated memory space address becomes smaller and smaller
  • Heap: Dynamically allocated space via alloc, malloc, calloc, etc. The allocated memory space addresses are getting larger and larger

In iOS, reference counting is used to manage the memory of OC objects

  • The default reference count of a newly created OC object is 1. When the reference count is reduced to 0, the OC object is destroyed, freeing up its memory
  • A call to retain gives the OC object’s reference count +1, and a call to release gives the OC object’s reference count -1

Summary of experience in memory management

  • When the alloc, new, copy, and mutableCopy methods return an object, release or autoRelease is called when the object is no longer needed
  • To own an object, make its reference count +1; If you don’t want to own an object, let its reference count be -1

Extern void _objc_autoreleasePoolPrint(void); extern void _objc_autoreleasePoolPrint

4. What does ARC do for LLVM + Runtime

  • LVVM generates the release code
  • RunTime is responsible for execution

The Runtime maintains a weak table, which 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

6. When will the autoRelease object be called release

  • IOS has registered two observers in the main thread Runloop
  • The first Observer listens for kCFRunLoopEntry and calls objc_autoreleasePoolPush().
  • The second Observer listens for the kCFRunLoopBeforeWaiting event. Objc_autoreleasePoolPop () will be called. Objc_autoreleasePoolPop () will be called if it listens for kCFRunLoopBeforeExit.The AutoReleased object was released when runloop was about to go to sleep

In the case of MRC, objects are freed when runloop is about to go to sleep

There are too many topics that can be refined in the article. I simply summarize a few questions here. If you want to know the specific implementation, please go to mygithubFind relevant articles to read. Welcome to like oh, if there is something in it that I understand is not quite correct, welcome to ask, we confirm each other