Runtime Learn to tidy up
object
- Object initialization exploration
- OC object initialization supplement and memory alignment exploration
- The nature of OC objects and ISA
class
- Class structure exploration
- Isa-related interview questions
- Cache_t structure analysis and underlying exploration
The message
- Runtime Runtime & Method essence & Fast method lookup
- Objc_msgSend slow lookup
- Dynamic method resolution
Application loading, class, and class initialization
- Dyld application loads
- Load and initialize analysis
- Objc_init () and read_images analysis
- Class load analysis
- Load analysis of classification
- Class extension and classification (category)
Related Interview Questions
1.load
andinitialize
Method call principles and call order?
-
The load method
load
Method during application loading (dyld
) complete the call atmain
Function before- At the bottom
load_images
When processing, two are maintainedload
Load tables, one for the class and the other for the class, with preference for the classload
Method invocation - In the class
load
Method is recursively processed to ensure that the parent class is processed first - so
load
Methods are called in the order of superclass, subclass, and classification - In the classification
load
Methods are called in the order in which they were compiled
-
The initialize method
initialize
It’s called when the first message is sent, soload
beforeinitialize
call- The way to classify is in class
realize
afterattach
What goes in is inserted in the front, so if it’s implemented in the categoryinitialize
Method will be called firstinitialize
methods initialize
The internal implementation is message sending, so if the subclass is not implementedinitialize
The superclass will be calledinitialize
Method is called twice- Because recursion is used internally, if both the subclass and the superclass implement
initialize
Method, then the parent class will be called first, and the child class will be called
See the concrete underlying implementation principle: Load and initialize analysis
-
Add c++ constructors
-
After analyzing dyld, you can determine the order of calls, load->c++->main
-
But if c++ is written in an objc project, when objc_init() is called, the c++ function will be called first via the static_init() method instead of waiting until _dyld_objc_notify_register registers load_images with dyld
-
At the same time, dyld is not required if objc_init() starts from itself, and it is possible for c++ functions to be called before load
-
2. What is Runtime?
Runtime
Is made up ofC
andC++
A set of assembly implementationsAPI
forOC
The language adds object-oriented, runtime functionality- The runtime (
Runtime
) refers to deferring data type determination from compile time to run time, such as the distinction between class extension and classification - Everyday written
OC
The code, as the program runs, actually ends up being converted toRuntime
theC
Language code,Runtime
是Object-C
Behind the scenes
3. The nature of the method,sel
What is?IMP
What is? What is the relationship between the two?
- The essence of the method: To send a message, the message will have the following processes:
- Quick lookup (
objc_msgSend
) ~cache_t
The cache information - Slow lookup ~ recurse itself or a parent class ~
lookUpImpOrForward
- Message not found: dynamic method parsing ~
resolveInstanceMethod
- The message is forwarded quickly ~
forwardingTargetForSelector
- The message is forwarded slowly ~
methodSignatureForSelector
和forwardInvocation
- Quick lookup (
sel
Is the method number, inread_images
It is compiled into memorytypedef struct objc_selector *SEL;
imp
That’s our function implementation pointer, findimp
It’s finding the functionsel
It’s like the table of contents of a booktittle
imp
It’s the page number of the book- To look for specific functions is to look for specific chapters in the book
- We first know what we want to see
tittle
(sel
) - According to the corresponding page number of the directory (
imp
) - Turn to the specific content method implementation
- We first know what we want to see
4. Is it possible to add instance variables to the compiled class? Can you add instance variables to classes created at run time?
-
You cannot add an instance variable to the compiled class
- Our compiled instance variable is stored at
ro
Once the compilation is complete, the memory structure is completely determined; - You can add methods and properties (associated objects) to classes by classification
- Our compiled instance variable is stored at
-
Instance variables can be added to classes created at runtime as long as they are not registered in memory
You can create classes at runtime with objc_allocateClassPair and add member variables and properties to them, as shown in the following code:
// Create a Class with objc_allocateClassPair const char * className = "SelClass"; Class SelfClass = objc_getClass(className); if (! SelfClass){ Class superClass = [NSObject class]; SelfClass = objc_allocateClassPair(superClass, className, 0); BOOL isSuccess = class_addIvar(SelfClass, "name", sizeof(NSString *)), log2(_Alignof(NSString *)), @encode(NSString *)); class_addMethod(SelfClass, @selector(addMethodForMyClass:), (IMP)addMethodForMyClass, "V@:");Copy the code
[super class] [self class] [super class] [super class]
In the following example, LGTeacher inherits from LGPerson. In the init initialization method of LGTeacher, we call [self class] and [super class].
// LGPerson
@interface LGPerson : NSObject
@end
@implementation LGPerson
@end
// LGTeacher
@interface LGTeacher : LGPerson
@end
@implementation LGTeacher
- (instancetype)init{
self = [super init];
if (self) {
NSLog(@"%@ - %@", [self class], [super class]);
}
return self;
}
@end
Copy the code
Analysis ideas:
Let’s make sure that LGPerson and LGTeacher do not currently implement a class method, so they will both end up calling NSObject’s instance method class, which is implemented as follows:
- (Class)class {
return object_getClass(self);
}
Copy the code
So both of these methods are going to return the class of self, so who is self? We know when we’re analyzing the nature of the method, the nature of the calling method is to send a message, objc_msgSend, with two hidden parameters, id self and SEL SEL, and the hidden parameter self is the type that we’re analyzing.
-
[self class] the output is LGTeacher. Because the sender of the message is a LGTeacher object, through the message sending mechanism, find NSObejct and call the class method, but the recipient of the message has not changed, it is still a LGTeacher object!
-
What does [super class] output? The same way clang, see CPP in the underlying implementation principle is what?
The super keyword finally uses the objc_msgSendSuper method in the bottom layer, and its recipient is (ID)self. The global search for objc_msgSendSuper logic is shown in the following figure:
The objc_super structure is as follows:
/// Specifies the superclass of an instance. struct objc_super { /// Specifies an instance of a class. __unsafe_unretained _Nonnull id receiver; /// Specifies the particular superclass of the instance to message. #if ! defined(__cplusplus) && ! __OBJC2__ /* For compatibility with old objc-runtime.h header */ __unsafe_unretained _Nonnull Class class; #else __unsafe_unretained _Nonnull Class super_class; #endif /* super_class is the first class to search */ };Copy the code
Select * from LGTeacher where id receiver is used and Class super_class is used. Select * from LGTeacher where id receiver is used and Class super_class is used. The objc_msgSendSuper method is called internally and the parameter objc_super is passed, where receiver is the LGTeacher object and super_class is the parent class of LGTeacher, which is the first class to look for.
That is, the recipient of [super Class] is still a LGTeacher object to call the methods of the superclass.
View the running structure:
Supplement:
Call objc_msgSendSuper, but actually call objc_msgSendSuper2 and why?
Search for objc_msgSendSuper globally and enter the assembly implementation process. In the assembly process, objc_msgSendSuper2 will eventually be called, as shown below:
5. A case of pointer translation and message sending?
For example, the LGPerson class has an instance method, saySomething, which can be called in viewDidLoad either by creating a LGPerson object or by bridging it.
- (void)viewDidLoad {
[super viewDidLoad];
LGPerson *person = [LGPerson alloc];
[person saySomething];
Class cls = [LGPerson class];
void *kc = &cls;
[(__bridge id)kc saySomething];
}
@implementation LGPerson
- (void)saySomething{
NSLog(@"%s - %@",__func__);
}
@end
Copy the code
Can the question be invoked successfully?
-
Analysis methods
First, the essence of method invocation is to send a message, find the class address through the object isa, carry out address translation, and find the corresponding method to implement IMP through SEL.
-
[person saySomething]; It certainly works that way
How does this process work? Find the corresponding class using the Person object’s ISA pointer, and shift the address in the class. First, do a quick lookup in cache_t, and if you can’t find it, then look in the method list and the parent class’s method list. In summary, take the address of the class as entry, shift the address, and find the IMP.
-
[(__bridge id)kc saySomething]; Is that ok?
Class CLS = [LGPerson Class]; What is CLS? CLS is a pointer, and the definition of Class is a pointer to a pointer to objc_class, in this case LGPerson. Assign the address of CLS to KC, where kc is the address of CLS, which also points to the class.
To sum up, the entry point of the two calls is the same, the method lookup process starts from the same address, it must be called, in addition to the address, person also has the memory data structure; Kc has only one address, which is a disguised Person object, as shown below:
Through LLDB debugging, we can find that kc points to the class, as shown in the following figure:
Run validation and both can be called successfully. See below:
-
-
To expand case
To modify the example above, the first property of the LGPerson object in the saySomething method is output in the following code:
- (void)viewDidLoad { [super viewDidLoad]; LGPerson *person = [LGPerson alloc]; person.kc_name = @"name123"; [person saySomething]; Class cls = [LGPerson class]; void *kc = &cls; [(__bridge id)kc saySomething]; } @interface LGPerson : NSObject @property (nonatomic, copy) NSString *kc_name; - (void)saySomething; @end @implementation LGPerson - (void)saySomething{ NSLog(@"%s - %@", __func__, self.kc_name); } @end Copy the code
Now what happens when we run this?
[person saySomething];
No questions about the output structure after the call[(__bridge id)kc saySomething];
What is the output structure of? See below:
According to LLDB debugging, person performs address translation to obtain the attribute kc_name. This data structure is in the heap, while KC is just an address. Obtaining kc data structure only outputs its data information in the stack. See below:
Runtime Interview questions, continue to update…