From the OC message mechanism, we already know that the message mechanism runs at Runtime. Today we’re going to take a quick look at what run-time can do for us.

concept

  • OC is a very dynamic language that allows many operations to be completed dynamically at runtime, rather than all compiled ahead of time.

  • Runtime is a SET of C language API, which encapsulates many functions related to dynamics. The dynamics of OC is supported and realized by Runtime

The OC code we wrote is converted into the Runtime API to call, and it can dynamically add methods, swap methods, create objects, iterate over private member variables, and so on.

Message mechanism

  • All method calls in OC are made through Runtimeobjc_msgSendThe function is done.objc_msgSendThe three steps of sending messages, dynamic method resolution and message forwarding are carried out internally to ensure the success of method invocation. This is the messaging mechanism. Details please see

How does the series-OC message mechanism work

As you can see from the message mechanism, the Runtime can add methods dynamically, change the receiver of a method (calling another object or class’s method), or have multiple objects handle a method call through message forwarding, and so on.

The Runtime API application

Methods exchange

  • Method swapping can also be called method swizzling – commonly known as “Dark magic”

  • Due to the runtime nature of Objective-C, the methods that an object calls when it receives a message are actually resolved at run time. Based on this feature, we can actually implement swaps in another way, a technique called “method swizzling”.

Method interaction uses this function

method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)

The two parameters of method_exchangeImplementations can be obtained using the following function

class_getInstanceMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name)

  • For example
#import <Foundation/Foundation.h>

@interface FRPerson : NSObject
- (void)methodOne;
- (void)methodTwo;
@end
Copy the code
#import "frperson. h" @implementation FRPerson - (void)methodOne{NSLog(@" actually call method 1 "); } - (void)methodTwo{NSLog(@" actually called methodTwo "); } @endCopy the code

#import <Foundation/Foundation.h>
#import "FRPerson.h"
#import <objc/runtime.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        FRPerson *person = [[FRPerson alloc] init];
        
        Method method1 = class_getInstanceMethod([person class], @selector(methodOne));
        
        Method method2 = class_getInstanceMethod([FRPerson class], @selector(methodTwo));
        
        
        method_exchangeImplementations(method1, method2);
        
        
        [person methodOne];//实际调用了方法二
        
        [person methodTwo];//实际调用了方法一
        
        
    }
    return 0;
}


Copy the code
  • This example is just an example of how method swapping is used, not in real development.

The most common is to add debugging information to the system methods or to extend the system methods.

There are also some extensions to third-party framework methods and so on.

For example, intercepting all button click events (creating a UIControl taxonomy)

#import "UIControl+Extension.h" #import <objc/runtime.h> @implementation UIControl (Extension) + (void)load { Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:)); Method method2 = class_getInstanceMethod(self, @selector(fr_sendAction:to:forEvent:)); method_exchangeImplementations(method1, method2); } - (void)fr_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {NSLog(@"%@-%@-%@ -%@", self, target, NSStringFromSelector(action)); [self fr_sendAction: Action to:target forEvent:event]; [self fr_sendAction:action to:target forEvent:event]; If ([self isKindOfClass:[UIButton Class]]) {// block all button events}} @endCopy the code

Dictionary model

  • useclass_copyIvarListFunction gets a list of member variables (including private ones)

There are a lot of properties in FRPerson.

#import <Foundation/Foundation.h>

@interface FRPerson : NSObject
@property (assign, nonatomic) int ID;
@property (assign, nonatomic) int weight;
@property (assign, nonatomic) int age;
@property (copy, nonatomic) NSString *name;
@end
Copy the code

Class NSObject extension to handle dictionary transformation model

#import <Foundation/Foundation.h>

@interface NSObject (Json)

+ (instancetype)fr_objectWithJson:(NSDictionary *)json;

@end
Copy the code
#import "NSObject+Json.h" #import <objc/runtime.h> @implementation NSObject (Json) + (instancetype)fr_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; } @endCopy the code

Create an object based on the extended class factory method

/ / dictionary model NSDictionary * json = @ {@ "id" : 20, @ @ "age" : 20, @ @ "weight" : 60, @ @ "name:" @ "Jack" / / @ "no" : 30 @}; FRPerson *person = [FRPerson fr_objectWithJson:json];Copy the code

Sets the color of UITextField placeholder text

  • useclass_copyIvarListFunction gets a list of member variables (including private ones)
unsigned int outCount; Ivar *ivars = class_copyIvarList([self.textField class], &outCount); for (int i = 0; i < outCount; i ++) { Ivar ivar = ivars[i]; const char *name = ivar_getName(ivar); const char *encoding = ivar_getTypeEncoding(ivar); NSLog(@"%s,--- %s", name, encoding); } // return the value of an overlay label as an empty placeholderLabelCopy the code
//placeholderLabel is lazy and initialized when the value is set. _textfield. placeholder = @" placeholder "; / / by KVC set value [_textField setValue: [UIColor brownColor] forKeyPath: @ "placeholderLabel. TextColor"].Copy the code

Runtime API

  • In addition to the above applications, there are many other functions, and some apis are listed here for your reference.
related
Create a class dynamically (parameter: Parent class, class name, Extra memory space) Class objc_allocateClassPair(Class superclass, const char *name, Void objc_registerClassPair(Class CLS) Destroy a Class void objc_disposeClassPair(Class) Class object_getClass(id obj); CLS; Class CLS) Determines whether an OC object is aClass BOOL object_isClass(ID obj) Determines whether aClass is a metaclass. BOOL class_isMetaClass(Class CLS) Retrieves the parent Class class_getSuperclass(Class cls)Copy the code
Member variable correlation
Ivar class_getInstanceVariable(Class CLS, Const char *name) Ivar *class_copyIvarList(Class CLS, Void object_setIvar(id obj, Ivar Ivar, id value) id object_getIvar(id obj, BOOL class_addIvar(Class CLS, const char * name, size_t size, BOOL class_addIvar(Class CLS, const char * name, size_t size, uint8_t alignment, Const char * types) Obtain information about member variables const char *ivar_getName(Ivar v) const char *ivar_getTypeEncoding(Ivar v)Copy the code
Attributes related to
Get a property objc_property_t class_getProperty(Class CLS, Const char *name) copy property list (free) objc_property_t *class_copyPropertyList(Class CLS, Unsigned int *outCount) BOOL class_addProperty(Class CLS, const char *name, Const objc_property_attribute_t *attributes, unsigned int attributeCount) void class_replaceProperty(Class CLS, const char *name, const objc_property_attribute_t *attributes, Unsigned int attributeCount) gets some information about an attribute const char *property_getName(objC_property_t property) const char *property_getAttributes(objc_property_t property)Copy the code
Methods the related
Method class_getInstanceMethod(CLS, SEL name) Method class_getInstanceMethod(CLS, SEL name) IMP class_getMethodImplementation(Class CLS, SEL name) IMP class_getMethodImplementation(Method m, IMP imp) void method_exchangeImplementations(Method m1, Method *class_copyMethodList(Class CLS, Unsigned int *outCount) BOOL class_addMethod(Class CLS, SEL name, IMP IMP, Const char *types) IMP class_replaceMethod(Class CLS, SEL name, IMP IMP, SEL method_getName(Method m) IMP method_getImplementation(Method m) 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 associated const char *sel_getName(SEL SEL) SEL sel_registerName(const char * STR) implement IMP with block as method imp_implementationWithBlock(id block) id imp_getBlock(IMP anImp) BOOL imp_removeBlock(IMP anImp)Copy the code

What is it like to create a serial-oc object

What is the flow of series -OC method call

Simple understanding of series-class and meta-class structures

How is series – KVO&KVC realized

On the structure and loading time of series-classification

Brief introduction to series-load and initialize call timing and practical use

How does serial-block work