Objective-c is a very dynamic programming language, which is very different from C, C++ and other languages. The dynamic nature of Objective-C is supported by the Runtime API. The Runtime API provides interfaces in C, and the source code is written in C++ assembly language
Isa,
Specific referenceIsa,
The structure of the class
View the source code
- The bits were found
- Class_rw_t: data = bits.data(); The data() method is called
- The definition of finding the data() function looks like this
- Class_rw_t *data() = bits & FAST_DATA_MASK is the value of bits and operation mask.
- Find the class_rw_t structure
- That’s all that’s in there
class_rw_t
- Methods, properties, and protocols in class_rw_t are two-dimensional arrays that can be read and written and contain the initial content of classes and classified content
class_ro_t
- Class_ro_t source definition found baseMethodList
- I also found baseMethods which are actually the same pointer to the same address.
- BaseMethodList, baseProtocols, Ivars, and baseProperties in class_ro_t are one-dimensional arrays that are read-only and contain the initial contents of the class
- Ro_t is created first and then rw_t is created
method_t
Type Encoding
Check out this article for detailsMethod_t,
Methods the cache
- The method is stored in a two-dimensional array, and the method is called by iterating through it and executing it, but it’s slow to call a method multiple times
Principle of method caching
Objc_msgSend Execution process
- The OC method calls are converted to the objc_msgSend function
- The execution process of objc_msgSend can be divided into three phases
- Message is sent
- Dynamic method parsing
- forward
Objc_msgSend Execution process – Source code read after
- Find flow reference
Objc_msgSend Process 01- Message sending
- If you’re looking for a method from class_rw_t
- Sorted binary search
- If it’s not sorted, go through it
- The Receiver finds the receiverClass through the ISA pointer
- ReceiverClass finds the SuperClass using the Superclass pointer
Message sending details
Objc_msgSend Execution Flow 02- Dynamic method Resolution
- Developers can implement the following methods to dynamically add method implementations
- +resolveInstanceMethod:
- +resolveClassMethod:
- After dynamic parsing, the “send message” process is restarted
- Search for methods from the receiverClass cache
Dynamic addition method
Dynamic method parsing
Objc_msgSend Execution process 03- Message forwarding
- Developers can customize any logic in the forwardInvocation: method
- The above methods have two versions of object method, class method (can be preceded by a plus sign +, can also be a minus sign -)
forward
Generate NSMethodSignature
The essence of the super
- [Super message] underlying implementation
- 1. Message recipients are still subclass objects
- 2. Start with the parent class to find the implementation of the method
- Class or return object_getClass(self) returns the receiver of the message
- superClass == return class_getSuperclass(object_getClass(self));
Intermediate code for LLVM (IR)
-
Objective-c is converted to Intermediate Representation by the LLVM compiler before it becomes machine code.
-
Intermediate code can be generated using the following command line instructions
- clang -emit-llvm -S main.m
-
Introduction of grammar
- @ – global variable
- % – Local variable
- Alloca – Allocates memory in the stack frame of the currently executing function and automatically frees memory when the function is returned to its caller
- I32 – a 32-bit 4-byte integer
- Alignment align –
- Load-read, store write
- Icmp – Comparison of two integer values, returns a Boolean value
- Br – Selects the branch and switches to the label based on the condition, similar to goto if it does not jump based on the condition
- Label – Code label
- Call – To call a function
For details, please refer to the official documentation: llvm.org/docs/LangRe…
- File transfer assembly
application
Runtime Application 01 – View private member variables
- Set the color of the UITextField placeholder text (but now ios seems to be able to get this value directly).
The self. The textField. Placeholder = @ “please enter the user name”; [self.textField setValue:[UIColor redColor] forKeyPath:@”_placeholderLabel.textColor”];
Application of Runtime 02 – Dictionary to model
- Use Runtime to iterate through all properties or member variables
- Use KVC to set the value
Basic implementation of MJ_Extention
+ (instancetype)mj_objectWithJson:(NSDictionary *)json { id obj = [[self alloc] init]; unsigned int count; Ivar *ivars = class_copyIvarList(self, &count); for (int i = 0; i < count; Ivar = ivars[I]; Ivar = ivars[I]; NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)]; [name deleteCharactersInRange:NSMakeRange(0, 1)]; // set value id value = json[name]; if ([name isEqualToString:@"ID"]) { value = json[@"id"]; } [obj setValue:value forKey:name]; } free(ivars); return obj; }Copy the code
Application of Runtime 02 – Replacement method implementation
- class_replaceMethod
- method_exchangeImplementations
MJPerson *person = [[MJPerson alloc] init]; // class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v"); class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{ NSLog(@"123123"); }), "v"); [person run]; MJPerson *person = [[MJPerson alloc] init]; Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run)); Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test)); method_exchangeImplementations(runMethod, testMethod); [person run];Copy the code
API
The Runtime API01 – class
- Create a class dynamically (parameters: parent class, class name, extra memory)
- Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
- Register a class (add member variables before class registration)
- void objc_registerClassPair(Class cls)
- Destroying a class
- void objc_disposeClassPair(Class cls)
Void testClass() {// Create Class Class newClass = objc_allocateClassPair([NSObject Class], "MJDog", 0); class_addIvar(newClass, "_age", 4, 1, @encode(int)); class_addIvar(newClass, "_weight", 4, 1, @encode(int)); class_addMethod(newClass, @selector(run), (IMP)run, "v@:"); // Register class objc_registerClassPair(newClass); // MJPerson *person = [[MJPerson alloc] init]; // object_setClass(person, newClass); // [person run]; // id dog = [[newClass alloc] init]; // [dog setValue:@10 forKey:@"_age"]; // [dog setValue:@20 forKey:@"_weight"]; // [dog run]; // // NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]); // Release objc_disposeClassPair(newClass) when this class is no longer needed; }Copy the code
- Gets the Class to which ISA points
- Class object_getClass(id obj)
- Set the Class to which ISA points
- Class object_setClass(id obj, Class cls)
- Modified the class object of the instance object
- Determines whether an OC object is Class
- BOOL object_isClass(id obj)
- Metaclass objects are special class objects
- Determines whether a Class is a metaclass
- BOOL class_isMetaClass(Class cls)
- Access to the parent class
- Class class_getSuperclass(Class cls)
Runtime API02 — Member variable
-
Gets an instance variable information
- Ivar class_getInstanceVariable(Class cls, const char *name)
-
Copy the list of instance variables (free is called at the end)
- Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
-
Sets and gets the value of a member variable
- void object_setIvar(id obj, Ivar ivar, id value)
- id object_getIvar(id obj, Ivar ivar)
-
Dynamically add member variables (registered classes cannot dynamically add member variables)
- BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
-
Gets information about member variables
- const char *ivar_getName(Ivar v)
- const char *ivar_getTypeEncoding(Ivar v)
Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age"); // NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar)); Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name"); // // MJPerson *person = [[MJPerson alloc] init]; // object_setIvar(person, nameIvar, @"123"); // object_setIvar(person, ageIvar, (__bridge id)(void *)10); // NSLog(@"%@ %d", person.name, person.age); // Number of member variables unsigned int count; Ivar *ivars = class_copyIvarList([MJPerson class], &count); for (int i = 0; i < count; Ivar = ivars[I]; Ivar = ivars[I]; NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar)); } free(ivars);Copy the code
Runtime API03 — Attributes
- Get a property
- objc_property_t class_getProperty(Class cls, const char *name)
- Copy the property list (free is called at the end)
- objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
- Dynamically adding properties
- BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- Dynamic substitution property
- void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
- Gets some information about the property
- const char *property_getName(objc_property_t property)
- const char *property_getAttributes(objc_property_t property)
Runtime API04 — Methods
- Get an instance method, class method
- Method class_getInstanceMethod(Class cls, SEL name)
- Method class_getClassMethod(Class cls, SEL name)
- Method to implement related operations
- IMP class_getMethodImplementation(Class cls, SEL name)
- IMP method_setImplementation(Method m, IMP imp)
- void method_exchangeImplementations(Method m1, Method m2)
- Copy the list of methods (free is called at the end)
- Method *class_copyMethodList(Class cls, unsigned int *outCount)
- Dynamic addition method
- BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
- Dynamic substitution method
- IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
- Get method information (with copy you need to call free)
- SEL method_getName(Method m)
- IMP method_getImplementation(Method m)
- const char *method_getTypeEncoding(Method m)
- unsigned int method_getNumberOfArguments(Method m)
- char *method_copyReturnType(Method m)
- char *method_copyArgumentType(Method m, unsigned int index)
- Selector correlation
- const char *sel_getName(SEL sel)
- SEL sel_registerName(const char *str)
- Implemented using blocks as methods
- IMP imp_implementationWithBlock(id block)
- id imp_getBlock(IMP anImp)
- BOOL imp_removeBlock(IMP anImp)
Runtime method exchange
- The IMP addresses of the two methods are swapped
- Swap addresses and clear the cache
- Runtime Hook utility classes
- UIControl+Extension
#import <UIKit/UIKit.h> @interface UIControl (Extension) @end #import "UIControl+Extension.h" #import <objc/runtime.h> @implementation UIControl (Extension) + (void)load {// hook: Hook Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:)); Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:)); method_exchangeImplementations(method1, method2); } - (void)mj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action)); // Call the original implementation [self mj_sendAction: Action to:target forEvent:event]; // [target performSelector:action]; // if ([self isKindOfClass:[UIButton class]]) {// // intercepts all button events // //}} @endCopy the code
- NSMutableArray+Extension
#import <Foundation/Foundation.h> @interface NSMutableArray (Extension) @end #import "NSMutableArray+Extension.h" #import <objc/runtime.h> @implementation NSMutableArray (Extension) + (void)load { static dispatch_once_t onceToken; Dispatch_once (&oncetoken, ^{ Class CLS = NSClassFromString(@"__NSArrayM"); Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:)); Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:)); method_exchangeImplementations(method1, method2); }); } - (void)mj_insertObject:(id)anObject atIndex:(NSUInteger)index { if (anObject == nil) return; [self mj_insertObject:anObject atIndex:index]; } @endCopy the code
- NSMutableDictionary+Extension
#import <Foundation/Foundation.h> @interface NSMutableDictionary (Extension) @end #import "NSMutableDictionary+Extension.h" #import <objc/runtime.h> @implementation NSMutableDictionary (Extension) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class cls = NSClassFromString(@"__NSDictionaryM"); Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:)); Method method2 = class_getInstanceMethod(cls, @selector(mj_setObject:forKeyedSubscript:)); method_exchangeImplementations(method1, method2); Class cls2 = NSClassFromString(@"__NSDictionaryI"); Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:)); Method method4 = class_getInstanceMethod(cls2, @selector(mj_objectForKeyedSubscript:)); method_exchangeImplementations(method3, method4); }); } - (void)mj_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key { if (! key) return; [self mj_setObject:obj forKeyedSubscript:key]; } - (id)mj_objectForKeyedSubscript:(id)key { if (! key) return nil; return [self mj_objectForKeyedSubscript:key]; } @endCopy the code