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