What is the runtime
Runtime is an API written in C, C++, and assembly that provides the runtime for OC.
Runtime: loads memory and provides runtime functionality (runtime dependent) Compile-time: compiles high-level languages (OC, Swift, Java, etc.) source code into recognized languages (machine language –> binary)
Underlying library relationship:
The nature of objects and methods
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *p = [[LGPerson alloc] init];
[p run];
}
return 0;
}
Copy the code
Clang compile, CD to the corresponding file, open terminal, enter the following command
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o testMain.c++
或
clang -rewrite-objc main.m -o test.c++
Copy the code
Open the generated testmain. c++ file, which is very long with tens of thousands of lines of code, and look at the main ones, as follows
It can be seen that the essence of an object is a structure, and the essence of a method is to send messages. Any method call can be translated as a call to the objc_msgSend method
Class methods and instance methods
The object
LGStudent *s = [[LGStudent alloc] init];
objc_msgSend(s, sel_registerName("run"));
Copy the code
Class method call
objc_msgSend(objc_getClass("LGStudent"), sel_registerName("run"));
Copy the code
Sending a message to a parent class (object method)
struct objc_super mySuper;
mySuper.receiver = s;
mySuper.super_class = class_getSuperclass([s class]);
objc_msgSendSuper(&mySuper, @selector(run));
Copy the code
Send a message to the parent class via objc_msgSendSuper with the structure pointer as the first argument.
Sending a message to a parent class (class method)
struct objc_super myClassSuper; myClassSuper.receiver = [s class]; // Current class myclasssuper. super_class = class_getSuperclass(object_getClass([s class])); // Class of the current class = metaclass objc_msgSendSuper(&myClassSuper, @selector(run));Copy the code
The Runtime can be called in three ways: Runtime API –> (class_, objC_, object_) NSObject API –> (isSelector, isMemberOfClass)
Note: Where do object methods exist? ==> Class instance methods where do class methods exist? In what form does a class method exist in a metaclass? ==> instance method
Message sending Objc_msgSend
Two ways:
- Fast cache find – through assembly
- slow
Objc_msgSend is written in a pool, efficient and C language can not be changed by writing a function, leaving unknown parameters, to jump to arbitrary Pointers, assembly can be implemented using registers.
Below enter the dry goods, source code to see how to find IMP, assembly part:
_objc_msgSend is used to determine whether the receiver recevier is null. If the receiver recevier is null, isa is returned. If the receiver is null, ISA is returned. There are three more cachehelookup results. If one is found, a CacheHit call or return IMP is performed. If it is the second kind of CheckMiss, the next function is called __objc_msgSend_uncached; The third is that if the IMP is found somewhere else, add it here for the next quick lookup.
Take a look at calls to __objc_msgSend_uncached:
__class_lookupMethodAndLoadCache3
As can be seen from the above code, this is a long search process, first from their own method list to find, if found, call, at the same time the IMP stored in the cache; If you don’t find it, you look in your parent class, and then you go back and forth, recursively looking in your parent class, until you find NSObject.
The dynamic analysis
The triedResolver variable makes dynamic resolution work only once. Focus on the _class_resolveMethod method:
The above code determines whether it is a metaclass. Instead of using the _class_resolveInstanceMethod method, the metaclass uses the _class_resolveClassMethod method.
+resolveClassMethod +resolveInstanceMethod +resolveClassMethod +resolveInstanceMethod
Let’s look at dynamic parsing in code:
@interface LGPerson : NSObject
- (void)run;
@end
@implementation LGPerson
#pragma mark - Dynamic method parsing
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSLog(@"Here we go, buddy.");
return [super resolveInstanceMethod:sel];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[[LGPerson alloc] run];
}
return 0;
}
Copy the code
Note that the LGPerson class does not implement the run instance method, nor does it implement the parent class or class. The resolveInstanceMethod: method is overridden in the.m file. Run the code
+ (BOOL)resolveInstanceMethod:(SEL)sel
triedResolver
SEL + (BOOL)resolveInstanceMethod (SEL) SEL add a breakpoint
_objc_msgSend_uncached
lookUpImpOrForward
_class_resolveInstanceMethod
imp
Let’s skip to the + (BOOL)resolveInstanceMethod (SEL) SEL method for the second time
If we are redirecting at this step, we can use the following method
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if(sel == @selector(run)) {// Dynamically parse our object method NSLog(@)"Object method parsing goes here.");
SEL readSEL = @selector(readBook);
Method readM= class_getInstanceMethod(self, readSEL);
IMP readImp = method_getImplementation(readM); // Get imp const char * for redirection methodstype = method_getTypeEncoding(readM);
return class_addMethod(self, sel, readImp, type); // Add method implementation}return [super resolveInstanceMethod:sel];
}
Copy the code
_class_resolveInstanceMethod () : _class_resolveInstanceMethod () : _class_resolveInstanceMethod Call _class_resolveInstanceMethod (resolveInstancemethod, resolveInstancemethod, resolveclassmethod, resolveclassmethod, resolveclassmethod)
Remember where class methods are stored? First, it is a class method of a class, and second, it is an instance method of a metaclass. So in the process of looking for class method IMP is more than a step, if in doubt, can be verified by the following code
ip
ip
// NSObject's classification verifies this problem by commenting out instance methods and class methods#import "NSObject+ZB.h"
#import <objc/runtime.h>
@implementation NSObject (ZB)
+ (void)run {
NSLog(@"NSObject === + run");
}
- (void)run {
NSLog(@"NSObject === - run"); } @zbPerson: NSObject + (void)run;} @zbPerson: NSObject + (void)run; Run int main(int argc, const char * argv[]) {@autoreleasepool {[ZBPerson run]; }return 0;
}
Copy the code
According to the above description, the compilation results are as follows
run
Class methods are stored in metaclasses as instance methods
What if I open the comment for the class method run? So let’s see what happens
run
imp
Remember the following diagram to clarify the position of ISA and superclass. (If there are any errors in the diagram, please also point out, thanks)
forward
When dynamic parsing doesn’t get the IMP we want, it returns NO and then goes to message forwarding.
The following shows the use of three methods in message forwarding
#pragma Mark - Message forwarding
- (id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
if(aSelector == @selector(run)) {// Forward to our ZBStudent objectreturn [ZBStudent new];
}
return [super forwardingTargetForSelector:aSelector];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
NSLog(@"%s",__func__);
if(aSelector = = @ the selector (run)) {/ / forwardingTargetForSelector only Method signature without implementation Method Method = class_getInstanceMethod(object_getClass(self), @selector(readBook));
const char *type = method_getTypeEncoding(method);
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
NSLog(@"-- -- -- -- -- - % @ -- -- -- -- --",anInvocation);
anInvocation.selector = @selector(readBook);
[anInvocation invoke];
}
Copy the code
These three methods, I believe you already very familiar with, method forwardingTargetForSelector: allows us to replace the message receiver for other objects, if this method returns nil or self, will send object methodSignatureForSelector: news, The method’s signature is used to generate the NSInvocation object, which is then translated into the forwardInvocation:, otherwise the object will be returned to resend the message.
The above is the complete message forwarding with the following figure
Many applications are implemented in this layer, but we will not discuss this now, we will focus on how these three methods come from, so continue to look at our source code
_objc_msgForward_impcache
How do you know that all three methods are called during message forwarding?
Introduced a method instrumentObjcMessageSends
extern void instrumentObjcMessageSends(BOOL);
int main(int argc, const char * argv[]) {
@autoreleasepool {
instrumentObjcMessageSends(YES);
[ZBPerson run];
instrumentObjcMessageSends(NO);
}
return 0;
}
Copy the code
InstrumentObjcMessageSends way is to print the current call a method invocation process, compile path can be in after the completion of the Macintosh HD/private/TMP/msgSends – XXXXX msgSends – XXXXX, view the files below