Nature of method
In the OC principle — Nature of Objects, Classes we see that all methods of classes and classifications are stored in a two-dimensional array of class_rw_ext_t->methods.
struct class_rw_ext_t { const class_ro_t *ro; method_array_t methods; // 2d method array property_array_t properties; Protocol_array_t protocols; Char *demangledName; uint32_t version; }; So method_array_t stores all of these method_list_t's, and method_list_t stores all of these method_t's. Struct method_t {SEL name; // The function can also be called const char *types; // Code (return value type, parameter type) MethodListIMP IMP; // function address};Copy the code
Methods with the same name in different classes have the same method selector
The types generation rules are as followsThere is a directive in iOS called @encode that converts a specific type into a string encoding
C --> A char I --> An int S --> A short L --> A longl is treated as A 32-bit quantity on 64-bit programs.q ---> A long long C ---> An unsigned char I ---> An unsigned int S ---> An unsigned short L ---> An unsigned long Q ---> An unsigned long long f ---> A float d ---> A double B ---> A C++ bool or a C99 _Bool v ---> A void * ---> A character string (char *) @ ---> An object (whether statically typed or typed id) # ---> A class object (Class) : ---> A method selector (SEL) [array type] ---> An array {name=type... } ---> A structure (name=type...) ---> A union bnum ---> A bit field of num bits ^type ---> A pointer to type ? ---> An unknown type (among other things, this code is used for function pointers)Copy the code
An 🌰
//types is v16@0:8 v: return type viod 16: all arguments take up 16 bytes @ : The first argument to the function is self and the type is id 0: the first argument starts from 0: indicates the second argument is _cmd, SEL type 8: The second argument starts at 8 -(void)test;Copy the code
Method caching mechanism
If the superclass pointer of the Class object or meta-class object is not found, the superclass pointer of the Class object or meta-class object is found. If the superclass pointer of the Class object or meta-class object is not found, the superclass pointer of the superclass object is found. Then find the parent class…
So hard to find the method, if I did not call again for a while, again!! Would it not be madness…
struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // Method cache class_data_bits_t bits; Class_rw_t *data() const {return bits.data(); }}Copy the code
So OC has a method caching mechanism: we see a method cache (type cache_t) inside the Class structure, which is used for method caching. Cache uses hash tables to cache previously called methods, which can improve method lookup speed.
struct cache_t { #if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED explicit_atomic<struct bucket_t *> _buckets; // The hash is an array of bucket_t explicit_atomic< mask_T > _mask; Struct bucket_t {explicit_atomic<uintptr_t> _imp; // explicit_atomic<SEL> _sel; // Function name}Copy the code
Hash tables can improve the speed of search, in fact, is a space for time means.
The general process is as follows:
- Storage process:
Call – (void) test; The @selecor and IMP of the method generate a bucket_t, and then use the @selector(test)&_mask bit operation to generate an index index. Store bucket_t in the _BUCKETS hash table in cache_T according to the generated index index.
- Reading process:
-(void)test; The @selector(test)&_mask method is used to generate an index index, which is fetched from the _buckets hash table in cache_T to bucket_t, and then invoked from IMP.
Taking the index value directly is definitely more efficient than traversing one by one, but the index value generated by bit operation will not increase successively, which will lead to some space in the hash table not being usedSpace for time
. A cached hash table might look like this.
- Q1: What should I do if the space of the created hash table is used up?
- A1: A hash table with space of 4 is created initially. When the number of methods stored reaches 3/4 of the current hash table, a new hash table is created, the storage space is doubled, the original hash table is cleared, and the methods have to be recached.
- Q2: @ the selector (XXX) & _mask?
- A2: This generates index<=_mask.
- Q3:
@selector(XXX)&_mask
The calculated index index is stored to find that it has been generated by other methodsbucket_t
What if it’s occupied?- A3: Generates a new index as follows
The index = index? The index - 1: mask
The new index is index-1. The new index is 0. The length of the hash table is -1. If the newly generated index is still occupied, recurse back to the new index. So the cache that you get might not be the cache that this method generated, so you recursively generate a new index and get it until you get the cache of this method, and of course when you hold the original index and get nil, that means this method hasn’t been cached yet, so you have to look up the method and cache it.
- A3: Generates a new index as follows
- Q4: Where is the method cached when calling a parent class? Where are the class methods cached?
- Q4: methods are cached in the method caller’s class object or metaclass object. The difference is that instance methods are cached in the class object and class methods are cached in the metaclass object.
Message mechanism
Method calls in OC are converted into objc_msgSend function calls, which send messages to method callers, so method callers are also called message receivers, and method names are also called message names.
The objc_msgSend execution process can be divided into three phases
- Message is sent
- Dynamic method parsing
- forward
1. Message sending process
Receiver is the method caller. Find receiverClass receiver through isa pointer, receiverClass superclass is found through a superclass pointer
2. Dynamic method parsing
-(void)other{ NSLog(@"other"); } resolveInstanceMethod:(SEL) SEL {if (SEL == @selector(test)) {resolveInstanceMethod:(SEL) SEL {if (SEL == @selector(test)) Method Method = class_getInstanceMethod(self, @selector(other)); this Method is added to the class object's class_rw_t list of methods. // This is the case where self is the class object. Notice here we add the method class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method)); return YES; } return [super resolveInstanceMethod:sel]; ResolveClassMethod :(SEL) SEL {if (SEL == @selector(test)) {resolveClassMethod:(SEL) SEL {if (SEL == @selector(test)) This Method is added to the list of methods on the metaclass object's class_rw_t. Method Method = class_getInstanceMethod(self, @selector(other)); Get the metaclass object by adding a method to the metaclass object class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method)); return YES; } return [super resolveClassMethod:sel]; }Copy the code
Note: We saw instance methods and class methods of dynamic method finally is to execute the – (void) other {}, instance methods and class methods is essentially the same, + – just the grammar of the OC, purpose is to put the method to a class object or instance objects, just get a function pointer call directly, need not care about methods in where.
After the dynamic method is resolved, the message sending process is repeated, starting with finding the method in the receiverClass cache. So dynamic parsing of instance methods must add methods to class objects, and dynamic parsing of class methods must add methods to meta-class objects, otherwise dynamic parsing will fail.
3. Message forwarding: Forwarding messages to others
The methods used in the above forwarding have two versions: object method and class method.
-(void)test:(int)age{NSLog(@"cat-test"); } @ the end / / message forwarding - (id) forwardingTargetForSelector (SEL) aSelector {if (aSelector = = @ the selector (the test:)) {return [new] Cat; / / will be the test message is forwarded to the Cat object} return [super forwardingTargetForSelector: aSelector]; } @selector(test:) in Person is the same as @selector(test:) in Cat. Return value type and parameter types - (NSMethodSignature *) methodSignatureForSelector (SEL) aSelector {if (aSelector = = @ the selector (test) {return [NSMethodSignature signatureWithObjCTypes:"v@:i"]; } return [super methodSignatureForSelector:aSelector]; } // The method invocation encapsulates a method invocation including: Method invocation, method name, method parameter -(void)forwardInvocation:(NSInvocation *)anInvocation{// Or you can do nothing and not call the parent class and it won't crash NSLog(@" FDF "); // Do the same thing: If (anInvocation. Selector == @selector(test:)) {// the method caller is the person object from the start of the anInvocation. // Invocation. Target = [[Cat alloc] init]; // [anInvocation invoke]; [Invocation invokeWithTarget:[Cat alloc] init]]; } [super forwardInvocation:anInvocation]; Invocation. Selector = @selector(XXX); [anInvocation invoke]; // If (anInvocation. Selector == @selector(test:)) {// Int age; // The invocation self and _cmd will not be used if the invocation fails. // NSLog(@"%d",age); // NString *name = @"zht"; // [anInvocation setArgument:&name atIndex:2]; / /}}Copy the code
forwardingTargetForSelector
: forwards the message to an object that can process the messagemethodSignatureForSelector
andforwardInvocation
The first method generates the method signatureNSMethodSignature
Object, and then createanInvocation
The object seat parameter is passed to the second method, and then in the second method canTreat with abandon
As long as the parent method is not executed in the second method, it will not crash even if it is not handled.