Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
Let’s first look at the resolveClassMethod method of a class object in method dynamic resolution.
static void resolveClassMethod(id inst, SEL sel, Class cls) { runtimeLock.assertUnlocked(); ASSERT(cls->isRealized()); ASSERT(cls->isMetaClass()); if (! lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) { return; } Class nonmeta; { mutex_locker_t lock(runtimeLock); nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst); if (! nonmeta->isRealized()) { _objc_fatal("nonmeta class %s (%p) unexpectedly not realized", nonmeta->nameForLogging(), nonmeta); } } BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend; bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel); IMP imp = lookUpImpOrNilTryCache(inst, sel, cls); }Copy the code
- The arguments:
inst
Class object:sel
: Class method not foundcls
: metaclass
- call
lookUpImpOrNilTryCache
Function, internal call_lookUpImpTryCache
Function to the current class objectresolveClassMethod
Method for slow message lookup- In NSObject, this method is implemented by default
- Class objects that inherit from NSObject are not intercepted by return
- call
getMaybeUnrealizedNonMetalClass
Function that verifies the relationship between the current class object and the metaclass and returns a normal class - The system USES
objc_msgSend
, sendresolveClassMethod
The message- The message receiver is a class object
- In the message body
SEL
forresolveClassMethod
- Argument is a class method that could not be found
- call
lookUpImpOrNilTryCache
Function to perform a slow message lookup on a class method that was not previously found- If the
resolveInstanceMethod
Successful processing, return the imp after processing - If no method is found, return
_objc_msgForward_impcache
Function address to enter the message forwarding process
- If the
Find getMaybeUnrealizedNonMetaClass function definition
static Class getMaybeUnrealizedNonMetaClass(Class metacls, id inst) { static int total, named, secondary, sharedcache, dyld3; runtimeLock.assertLocked(); ASSERT(metacls->isRealized()); total++; if (! metacls->isMetaClass()) return metacls; if (metacls->ISA() == metacls) { Class cls = metacls->getSuperclass(); ASSERT(cls->isRealized()); ASSERT(! cls->isMetaClass()); ASSERT(cls->ISA() == metacls); if (cls->ISA() == metacls) return cls; } if (inst) { Class cls = remapClass((Class)inst); while (cls) { if (cls->ISA() == metacls) { ASSERT(! cls->isMetaClassMaybeUnrealized()); return cls; } cls = cls->getSuperclass(); } #if DEBUG _objc_fatal("cls is not an instance of metacls"); #else // release build: be forgiving and fall through to slow lookups #endif } ... }Copy the code
- Determine the CLS, if not metaclass, and return it directly
- If CLS isa metaclass and isa points to itself, prove that the current CLS is the root metaclass, get its parent NSObject and return
- Iterate through the current class and its parent to find the class to which ISA points to the metaclass
- If both are found,
DEBUG
In mode, error message: the current class object is not an instance of the metaclass Release
In mode, follow the following process to find the class object of the metaclass- Check whether a metaclass has a pointer to its non-metaclass
- By metaclass
mangledName
Find the class object and return it if it exists and ISA points to a metaclass - Look for class objects in the global Map and return them if they exist
- In dyld
closure table
Find the class object in, return it if it exists - Look for class objects in the shared cache and return them if they exist
- None of the above processes was found, error message: there is no class pointing to this metaclass
Example in lgPerson.h, declare the sayNB class method
#import <Foundation/Foundation.h>
@interface LGPerson : NSObject
+(void)sayNB;
@end
Copy the code
In lgPerson. m, say666 instance method and resolveInstanceMethod class method are implemented, but sayNB instance method is not implemented
#import "lgPerson. h" #import <objc/runtime.h> @implementation LGPerson -(void)say666{NSLog(@" instantiation-say666 "); } + (BOOL)resolveClassMethod:(SEL) SEL {if(SEL ==@selector(sayNB)){NSLog(@"resolveClassMethod: %@, %@", self, NSStringFromSelector(sel)); IMP imp = class_getMethodImplementation(self, @selector(say666)); Method methodSay666 = class_getInstanceMethod(self, @selector(say666)); const char *type = method_getTypeEncoding(methodSay666); const char * c = NSStringFromClass(self).UTF8String; return class_addMethod(objc_getMetaClass(c), @selector(sayNB), imp, type); } return [super resolveClassMethod:sel]; } @endCopy the code
- If the class method called is
sayNB
, dynamic addsayNB
Method, and populate imp withsay666
Function address of - Due to the need to add
sayNB
Is a class method, so it needs to be added in the metaclass
In the main function, call the sayNB class method of LGPerson
int main(int argc, const char * argv[]) { @autoreleasepool { [LGPerson sayNB]; } return 0; } ------------------------- // Output: example method -say666Copy the code
- Automatically entered into
resolveClassMethod
methods