Constantly updated, the latest content to view the PDF version, latest: 2018-02-23

Part ONE: Conclusions from method calls

1.1 Text description: First look at a piece of code:

  1. The ClangClass class has a handle method
  2. The handle method creates an AClass object and calls the addO method
  3. Let’s look at the process of calling the addO method

1.2 Description of source code:

#import "ClangClass.h"
#import "AClass.h"

@interface ClangClass()
@property(nonatomic,strong)AClass *acClass;
@end

@implementation ClangClass

- (instancetype)init{
    if (self = [super init]) {
        _acClass = [[AClass alloc] init];
    }
    
    return  self;
}

- (void)handle{
    [_acClass addO];
}

@end
Copy the code

1.3 Operate the code:

  1. Go to the clangclass. m folder above and compile using clang-rewrite-objc clangclass.m
  2. Clangclass.cpp: [_acClass addO];
  3. {does it feel like a lot of mess, I think so too}
static void _I_ClangClass_handle(ClangClass * self, SEL _cmd) { ((void (*)(id, SEL))(void *)objc_msgSend)((id)(*(AClass **)((char *)self + OBJC_IVAR_$_ClangClass$_acClass)), sel_registerName("addO")); } objc_msgSend(_acClass, sel_registerName("addO")); -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- IMP: nature is a function pointer, preserved the way address, refers to the implementation method, which is generated by the compiler. /// A pointer to the function of a method implementation. #if ! OBJC_OLD_DISPATCH_PROTOTYPES typedef void (*IMP)(void /* id, SEL, ... * /); # else typedef id -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- come to the conclusion: 1. Calling the addO method calls objc_msgSend for message passing; 2. Objc_msgSend is to find IMP and execute the implementation code.Copy the code

Part 2: Message passing objc_msgSend, objc_msgSendSuper, objc_msgSend_stret, objc_msgSendSuper_stret

Objc_msgSend definition: id objc_msgSend(id self, SEL op... ; Self: A pointer that points to the instance of the class that is to receive the message. The selector of The method that handles The message.{selector}... : A variable argument list containing the arguments to the method. Typedef struct objc_object * ID; typedef struct object *id; struct objc_object { Class isa OBJC_ISA_AVAILABILITY; } The essence of SEL: SEL is the system generates a unique ID number to distinguish the method according to the method name and parameter sequence during compilation; This ID is SEL type. Unlike C's function pointer, which holds the address of the method directly, SEL is just the method number. Typedef struct objc_selector *SELCopy the code

2.1 Description of objc_msgSend workflow:

  1. Find the AClass class object along isa in self;
  2. Search for addO methods in AClass cache first, if there is an addO method return IMP;
  3. If the addO method is not found in the cache the first time, the class is checked to see if it has been freed.
  4. If the class is not released, check if it implements the +initialize method. If it does, initialize it.
  5. After judging the initialization, try to find the addO method in AClass cache again. If it cannot find the addO method, go to AClass methodLists to find the addO method.
  6. If the addO method is not found in AClass, it will look in the cache in super_class, which is NSObject. If it cannot be found in the parent’s cache, look for the addO method in the parent’s methodLists.
  7. If the addO method is also not found in the parent’s methodLists, and if you force resolver as YES and imp is not found, try _class_resolveMethod once, and the message forwarding is not attempted.
  8. If resolver is set to NO and IMP is not found, return _objc_msgForward_impcache to start message forwarding.
  9. Whichever step above finds an IMP returns the IMP directly.

2.2 related:

  • _objc_msgForward is used for message forwarding. The implementation of this function is not in objC-Runtime’s open source code, but in the Foundation framework. After adding a breakpoint to start the program, you see that the __CFInitialize method calls the objc_setForwardHandler function to register an implementation.
  • When sending a message to a generic object, call objc_msgSend; When a message is sent to super, objc_msgSendSuper is called; If the return value is a structure, either objc_msgSend_stret or objc_msgSendSuper_stret is called.

2.3 nature:

  • The essence of objc_msgSend is to find the IMP pointer to execute the code, whereas the IMP function pointer for static languages is determined at compile time, and dynamic languages can be changed after compile.

2.4 Source code description objc_msgSend workflow:

} id objc_msgSend(id self, SEL op...) { if (! self) return nil; IMP imp = class_getMethodImplementation(self->isa, SEL op); imp(self, op, ...) ; // Call this function, pseudo code... } // source code IMP class_getMethodImplementation(Class CLS, SEL SEL) {IMP IMP; if (! cls || ! sel) return nil; imp = lookUpImpOrNil(cls, sel, nil, YES/*initialize*/, YES/*cache*/, YES/*resolver*/); // Translate forwarding function to C-callable external version if (! Imp) {return _objc_msgForward; } return imp; } IMP lookUpImpOrNil(Class cls, SEL sel, id inst, bool initialize, bool cache, Imp = lookUpImpOrForward(CLS, SEL, inst, initialize, cache, resolver); // Imp = lookUpImpOrForward(CLS, sel, inst, initialize, cache, resolver); if (imp == _objc_msgForward_impcache) return nil; else return imp; } IMP lookUpImpOrForward(Class cls, SEL sel, id inst, bool initialize, bool cache, bool resolver) { Class curClass; IMP methodPC = nil; Method meth; bool triedResolver = NO; methodListLock.assertUnlocked(); MethodPC = _cache_getImp(CLS, sel); // Optimistic cache lookup if (cache) { if (methodPC) return methodPC; } // Check for freed classes if (CLS == _class_getFreedObjectClass()) Return _freedHandler return (IMP) _freedHandler; // Check for +initialize if (initialize &&! cls->isInitialized()) { _class_initialize (_class_getNonMetaClass(cls, inst)); // If sel == initialize, _class_initialize will send +initialize and // then the messenger will send +initialize again after this // procedure finishes. Of course, if this is not being called // from the messenger then it won't happen. 2778172 } // The lock is held to make method-lookup + cache-fill atomic // with respect to method addition. Otherwise, a category could // be added but ignored indefinitely because the cache was re-filled // with the old value after the Cache flush on behalf of the category. retry: // add methodListlock. lock(); MethodPC = _cache_getImp(CLS, sel); if (methodPC) goto done; Meth = _class_getMethodNoSuper_nolock(CLS, sel); meth = _class_getMethodNoSuper_nolock(CLS, sel); if (meth) { log_and_fill_cache(cls, cls, meth, sel); methodPC = method_getImplementation(meth); goto done; } // Try superclass caches and method lists. curClass = cls; While ((curClass = curClass->superclass)) {// superclass cache. Meth = _cache_getMethod(curClass, sel); _objc_msgForward_impcache); if (meth) { if (meth ! = (Method)1) {// Found the Method in a superclass. Cache it in this class. curClass, meth, sel); methodPC = method_getImplementation(meth); goto done; } else { // Found a forward:: entry in a superclass. // Stop searching, but don't cache yet; call method // resolver for this class first. break; Meth = _class_getMethodNoSuper_nolock(curClass, sel); if (meth) { log_and_fill_cache(cls, curClass, meth, sel); methodPC = method_getImplementation(meth); goto done; } } // No implementation found. Try method resolver once. if (resolver && ! TriedResolver) {// unlock methodListLock.unlock(); _class_resolveMethod(CLS, sel, inst); triedResolver = YES; goto retry; } // No implementation found, and method resolver didn't help. _cache_addForwardEntry(CLS, sel); // return _objc_msgForward_impcache to forward methodPC = _objc_msgForward_impcache; Done: // unlock methodListLock.unlock(); return methodPC; }Copy the code

Part three: message forward _objc_msgForward

// add1 method does not exist, (void)touchesBegan:(NSSet< uittouch *> *)touches withEvent:(UIEvent *)event{AClass *acClass = [[AClass alloc] init]; [acClass performSelector:@selector(add1)]; }Copy the code

3.1 Oc Forwarding Text Description:

  1. Call the resolveInstanceMethod: method. Allows users to dynamically add implementations to the Class at this point. If there is an implementation, the objc_msgSend process is restarted by calling and returning YES. This time the object responds to the selector, usually because it has already called class_addMethod. If not, proceed with the following action.
  2. If the call resolveInstanceMethod no implementation, they began to call forwardingTargetForSelector: method, trying to find a can respond to the message object. If it does, the message is forwarded directly to it, returning a non-nil object. Otherwise return nil and continue. Be careful not to return self here, otherwise you’ll end up in an endless loop.
  3. If the call forwardingTargetForSelector doesn’t work, began to call methodSignatureForSelector: method, trying to get a method signature. If not, call doesNotRecognizeSelector directly to throw an exception. Returns non-nil if it can be retrieved; To a NSInvocation and to forwardInvocation:.
  4. Wrap the method signature from step 3 as the Invocation passed in, call the forwardInvocation: method, and that’s where the processing goes, and return non-nil.
  5. DoesNotRecognizeSelector:, the default implementation is to throw an exception. If step 3 fails to obtain a method signature, perform this step.

3.2 OC Forwarding source code description:

Using instrumentObjcMessageSends method to check the log information of app, the specific method of use: the appdelegate. M which are defined as follows:  #import "AppDelegate.h" #import <objc/runtime.h> void instrumentObjcMessageSends(); @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. instrumentObjcMessageSends(YES); return YES; + AClass NSObject initialize + AClass NSObject alloc - AClass AClass init - NSObject NSObject init - AClass NSObject performSelector: + AClass NSObject resolveInstanceMethod: + AClass NSObject resolveInstanceMethod: - AClass NSObject forwardingTargetForSelector: - AClass NSObject forwardingTargetForSelector: - AClass NSObject methodSignatureForSelector: - AClass NSObject methodSignatureForSelector: - AClass NSObject class - AClass NSObject doesNotRecognizeSelector: - AClass NSObject doesNotRecognizeSelector: - AClass NSObject classCopy the code

The fourth part respondsToSelector, instancesRespondToSelector

// add1 method does not exist, now try forwarding, (void)touchesBegan:(NSSet< touch *> *)touches withEvent:(UIEvent *)event{AClass *acClass = [[AClass alloc] init]; [acClass respondsToSelector:@selector(add1)]; }Copy the code

4.1 Text Analysis:

  1. If the IMP method is not found, try resolveInstanceMethod once, with no message forwarding
  2. Message forwarding will not be carried out, so even if no method can be found, doesNotRecognizeSelector, which can not find a method, will not be reported as an error
  3. And the main reason, if you look at the source code for Response to Selector, is down here

4.2 Source code analysis:

+ (BOOL)respondsToSelector:(SEL)sel { if (! sel) return NO; return class_respondsToSelector_inst(object_getClass(self), sel, self); } - (BOOL)respondsToSelector:(SEL)sel { if (! sel) return NO; return class_respondsToSelector_inst([self class], sel, self); } --------------------------------------------- + (BOOL)instancesRespondToSelector:(SEL)sel { if (! sel) return NO; return class_respondsToSelector(self, sel); } --------------------------------------------- BOOL class_respondsToSelector(Class cls, SEL sel) { return class_respondsToSelector_inst(cls, sel, nil); } bool class_respondsToSelector_inst(Class cls, SEL sel, id inst) { IMP imp; if (! sel || ! cls) return NO; // Avoids +initialize because it historically did so. // We're not returning a callable IMP anyway. // The resolver passes YES, so when no method is found, the resolveInstanceMethod method is executed only once, avoiding message forwarding. Imp = lookUpImpOrNil(CLS, sel, inst, NO/*initialize*/, YES/*cache*/, YES/*resolver*/); return bool(imp); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / respondsToSelector forward part of the log + AClass NSObject + AClass the initialize NSObject alloc - AClass AClass init - NSObject NSObject init - AClass NSObject respondsToSelector: - AClass NSObject class + AClass NSObject resolveInstanceMethod: + AClass NSObject resolveInstanceMethod: - AClass NSObject deallocCopy the code

The fifth part methodForSelector, instanceMethodForSelector

// add1 method does not exist, now try forwarding, (void)touchesBegan:(NSSet< touch *> *)touches withEvent:(UIEvent *)event{AClass *acClass = [[AClass alloc] init]; [acClass methodForSelector:@selector(add1)]; } --------------------------------------------- + (IMP)methodForSelector:(SEL)sel { if (! sel) [self doesNotRecognizeSelector:sel]; return object_getMethodImplementation((id)self, sel); } - (IMP)methodForSelector:(SEL)sel { if (! sel) [self doesNotRecognizeSelector:sel]; return object_getMethodImplementation(self, sel); } --------------------------------------------- + (IMP)instanceMethodForSelector:(SEL)sel { if (! sel) [self doesNotRecognizeSelector:sel]; return class_getMethodImplementation(self, sel); }Copy the code

5.1 Look at the source can know, is the same implementation mechanism and respondsToSelector: no message forward

+ AClass NSObject initialize
+ AClass NSObject alloc
- AClass AClass init
- NSObject NSObject init
- AClass NSObject methodForSelector:
+ AClass NSObject resolveInstanceMethod:
+ AClass NSObject resolveInstanceMethod:
- AClass NSObject dealloc
Copy the code

Write: By Coderiding \ by Erliucxy \ Dongdong – Zhongshan – Guangdong 2018.2.12 5:25:51