The Runtime based

1.Runtime related header files

To learn the Runtime source code first-hand, look at the following three header files.

#import <objc/runtime.h>
#import <objc/message.h>
#import <objc/objc.h>
Copy the code

Runtime several important structures

As you can see from the structure, there is only one ISA pointer inside the instance, pointing to its own class.

/ / instance
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
Copy the code

The following is a description of the structure of the class.

/ / class
struct objc_class {
    // The isa pointer to the class points to its own metaclass
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
#if! __OBJC2__// super_class refers to the superclass
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    // Member variables
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    / / method
    struct objc_method_list * _Nullable * _Nullable** methodLists                    OBJC2_UNAVAILABLE;
    // Method cache
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    // The protocol to follow
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
Copy the code

Class defines class-related properties in the structure of the class. The following two structures are highlighted: instance variable array and method array. Objc_ivar_list Array of member variables

// Member variables
struct objc_ivar {
    / / variable names
    char * _Nullable ivar_name                               OBJC2_UNAVAILABLE;
    // Variable type
    char * _Nullable ivar_type                               OBJC2_UNAVAILABLE;
    int ivar_offset                                          OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
}                                                            OBJC2_UNAVAILABLE;
// Can be thought of as an array, the array element is objc_ivar
struct objc_ivar_list {
    int ivar_count                                           OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]                            OBJC2_UNAVAILABLE;
}
Copy the code

Objc_method_list Array of methods

struct objc_method {
    / / the method name
    SEL _Nonnull method_name                                 OBJC2_UNAVAILABLE;
    char * _Nullable method_types                            OBJC2_UNAVAILABLE;
    // The function that the method actually executes
    IMP _Nonnull method_imp                                  OBJC2_UNAVAILABLE;
}                                                            OBJC2_UNAVAILABLE;
// Can be thought of as an array of methods, each element objc_method
struct objc_method_list {
    struct objc_method_list * _Nullable obsolete             OBJC2_UNAVAILABLE;
    int method_count                                         OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space                                                OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]                        OBJC2_UNAVAILABLE;
}
Copy the code

3. Correlation function

3.1 Obtaining the Class Name

Get the class name dynamically from a class, but class_getName returns a C string that needs to be converted to OC via NSString.

+ (NSString *)fetchClassName:(Class)class {
    const char *className = class_getName(class);
    return [NSString stringWithUTF8String:className];
}
Copy the code

3.2 Obtaining member Variables

The following fetchIvarList gets the list of instance variables via class_copyIvarList, then ivar_getName gets the variable name, ivar_getTypeEncoding gets the variable type, and saves the variable name and type into an array via a dictionary.

+ (NSArray *)fetchIvarList:(Class)class {
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(class, &count);
    NSMutableArray *mutableList = [NSMutableArray new];
    for (int i = 0; i < count; i++) {
        NSMutableDictionary *dictM = [NSMutableDictionary new];
        const char *ivarName = ivar_getName(ivarList[i]);
        const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
        dictM[@"type"] = [NSString stringWithUTF8String:ivarType];
        dictM[@"ivarName"] = [NSString stringWithUTF8String:ivarName];
        [mutableList addObject:dictM];
    }
    // Remember to release it, otherwise memory leaks
    free(ivarList);
    return [NSArray arrayWithArray:mutableList];

}
Copy the code

3.3 Obtaining Attributes

Class_copyPropertyList gets an array of member variables. Property_getName gets the name of the variable. The method calls are very similar.

+ (NSArray *)fetchPropertyList:(Class)class {
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList(class, &count);
    NSMutableArray *mutableList = [NSMutableArray new];
    for (int i = 0; i < count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        [mutableList addObject:[NSString stringWithUTF8String:propertyName]];
    }
    // Also need to release
    free(propertyList);
    return [NSArray arrayWithArray:mutableList];
}
Copy the code

3.4 Getting instance methods of a class

The enclosed fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList fetchMethodList And then I get the method name by NSStringFromSelector.

+ (NSArray *)fetchMethodList:(Class)class {
    unsigned int count = 0;
    Method *methodList = class_copyMethodList(class, &count);
    NSMutableArray *mutalbelist = [NSMutableArray new];
    for (int i = 0; i < count; i++) {
        SEL methodSel = method_getName(methodList[i]);
        NSString *methodName = NSStringFromSelector(methodSel);
        [mutalbelist addObject:methodName];
    }
    free(methodList);
    return [NSArray arrayWithArray:mutalbelist];
}
Copy the code

3.5 Dynamic Adding Method

Dynamic method addition may be used less in the actual development, but in the message forwarding and crash processing can dynamically add methods to the object, to prevent program crash. The following is a method that adds a run to the current instance self via class_addMethod. The run method is just an SEL, and the actual function is myRun; The first parameter is the class to which the method needs to be added, the second parameter is the name of the method to be added, the third parameter is the function that the method actually executes, and the fourth parameter is the @encode instruction of the method parameter. Please refer to this section for the introduction of @encode instruction. V :void @ object ->self -> SEL->_cmd

class_addMethod(object_getClass(self), @selector(run:), (IMP)myRun, "v@:");
[self performSelector:@selector(run:) withObject:@"Ha ha"];
Copy the code
// IMP must take at least two arguments self and _cmd, other arguments can be added after the comma separated
void myRun(id self, SEL _cmd, id obj) {
    NSLog(@"sel:%@, obj:%@", NSStringFromSelector(_cmd), obj);
}
// Print the following result
selRun: obj: ha haCopy the code

3.6 Method Exchange

Method interchange is widely used in the whole Apple system and is often used in our actual development. The detailed usage will be introduced in the Runtime application scenario. Class_getInstanceMethod can find the corresponding Method in the class by using SEL selector.class_getInstancemethod And then call method_exchangeImplementations so we can implement the exchange of the two methods.

+ (void)methodSwap:(Class)class firstSel: (SEL)firstSel secondSel: (SEL)secondSel {
    Method firstMethod = class_getInstanceMethod(class.firstSel);
    Method secondMethod = class_getInstanceMethod(class.secondSel);
    method_exchangeImplementations(firstMethod, secondMethod);
}
Copy the code

We can just test it in the ViewController and see if it’s really interchangeable, right? Swap the two methods in the load method. From the printed information, you can see that the order in which the two methods are called has actually been reversed, indicating that the methods have indeed been switched.

+ (void)load {
    [super load];
    [self methodSwap:self firstSel: @selector(sayHello1) secondSel: @selector(sayHello2)];
}
- (void)viewDidLoad {
    [self sayHello1];
    [self sayHello2];
}
- (void)sayHello1 {
    NSLog(@"sayHello1");
}
- (void)sayHello2 {
    NSLog(@"sayHello2");
}
// Print the following result
2021-10-20 18:09:18.280476+0800 abffff[5660:200359] sayHello2
2021-10-20 18:09:18.280632+0800 abffff[5660:200359] sayHello1
Copy the code