directory

1. The Runtime API

2. The application of the Runtime

3


1. The Runtime API

Related:

 // Dynamically create a class (arguments: parent class, class name, extra memory)
 Class objc_allocateClassPair(<#Class  _Nullable __unsafe_unretained superclass#>, <#const char * _Nonnull name#>, <#size_t extraBytes#>)Example: the Class myClass= objc_allocateClassPair([NSObject class], "MyClass".0);
 
 // Register a class (add member variables before class registration)
 void objc_registerClassPair(Class cls) 

 // Destroy a class
 void objc_disposeClassPair(Class cls)
 
 // Get 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)
 
 // Determine if an OC object is Class
 BOOL object_isClass(id obj)
 
 // Determine whether a Class is a metaclass
 BOOL class_isMetaClass(Class cls)
 
 // Get the parent class
 Class class_getSuperclass(Class cls)
 
Copy the code

Member variable correlation:

// Get an instance variable
Ivar class_getInstanceVariable(Class cls, const char *name)

// Copy the list of instance variables.
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

// Sets and gets the values of member variables
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)

// Add member variables dynamically (registered classes cannot add member variables dynamically)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)

// Get information about member variables
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)

Copy the code

Attribute correlation:

// Get an attribute
objc_property_t class_getProperty(Class cls, const char *name)

// Copy the property list (call free at the end)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

// Add attributes dynamically
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                  unsigned int attributeCount)

// Dynamically replace attributes
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                      unsigned int attributeCount)

// Get some information about attributes
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)

Copy the code

Methods related:

// Get an instance method, class method
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

The // method implements the 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 (call free at the end)
Method *class_copyMethodList(Class cls, unsigned int *outCount)

// Add method dynamically
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

// Dynamic replacement method
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

// Get the information about the method (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)

// use block as a method
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
Copy the code

2. The application of the Runtime

1. Extend attributes for categories

In some scenarios, the properties provided by the system class do not meet our business requirements, so we can use the Runtime to add properties to the system class, such as adding a dictionary property to UIButton:

//UIButton+Category.h
#import <UIKit/UIKit.h>
@interface UIButton (Category)

@property(nonatomic,strong)NSDictionary * dict;

@end

//UIButton+Category.m
#import "UIButton+Category.h"
#import <objc/runtime.h>

@implementation UIButton (Category)

-(void)setDict:(NSDictionary *)dict{
    objc_setAssociatedObject(self.@selector(dict), dict, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


-(NSDictionary*)dict{
    return objc_getAssociatedObject(self, _cmd);
}
@end

/ / use
#import "UIButton+Category.h"
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton * button = [UIButton buttonWithType: UIButtonTypeCustom];
    button.dict =@ {@"key": @"value"};
    NSLog(@"% @",button.dict[@"key"]);  
}

Copy the code

The Runtime associative objects are used here, as described in the last section of this article

2. Traversing all member variables of the class (dictionary to model, automatic archive to unfile)

The Runtime API obtains the class variable name and KVC to implement a simple dictionary model function

//NSObject+Model.h
#import <Foundation/Foundation.h>
@interface NSObject (Model)

+ (instancetype)ModelWithDict: (NSDictionary *)dict;

- (void)transformDict:(NSDictionary *)dict;
@end

//NSObject+Model.m
#import "NSObject+Model.h"
#import <objc/runtime.h>

@implementation NSObject (Model)
+ (instancetype)ModelWithDict: (NSDictionary *)dict {
    NSObject * obj = [[self alloc]init];
    [obj transformDict:dict];
    return obj;
}
- (void)transformDict:(NSDictionary *)dict {
    Class cla = self.class;
    // count: number of member variables
    unsigned int outCount = 0;
    // Get an array of member variables
    Ivar *ivars = class_copyIvarList(cla, &outCount);
    // Iterate over all member variables
    for (int i = 0; i < outCount; i++) {
        // Get a member variable
        Ivar ivar = ivars[i];
        // Get the member variable name
        NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // Convert member variable names to attribute names (without the underscore _)
        key = [key substringFromIndex:1];
        // Retrieve the dictionary value
        id value = dict[key];
        // If the number of model attributes is greater than the dictionary key-value pair, the model attributes will be assigned nil and an error will be reported
        if (value = = nil) continue;
        // Use KVC to set the values in the dictionary to the model
        [self setValue:value forKeyPath:key];
    }
    // The pointer needs to be released because ARC does not apply to C functions
    free(ivars);
}
@end

/ / call:
- (void)viewDidLoad {
    [super viewDidLoad];
    NSDictionary *dict = @{
        @"name" : @"lanlin"The @"age" : @18The @"sex" : @"Male"
    };
    Student * student = [Student ModelWithDict:dict];
    NSLog(@"%@,%d,%@",student.name,student.age,student.sex);
    // Print results: Lanlin,18, male
}
Copy the code

The same runtime API can also be applied to object files:

// Unfile to use
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([self class].&count);
        for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [aDecoder decodeObjectForKey:key];
        if(value){
        [self setValue:value forKey:key];
       }
    }
    free(ivars);
  }
    return self;
}

// Archive use
- (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class].&count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        if(value){
        [aCoder encodeObject:value forKey:key];
     }
   }
   free(ivars);
}
Copy the code
4. Method exchange

For more details on Method swapping, see The Runtime Principles and Practices: Method Swapping, which uses the examples in this article for analysis.

Count VC loading times and print

UIViewController+Logging.m

#import "UIViewController+Logging.h"
#import <objc/runtime.h>

@implementation UIViewController (Logging)

// When to use: in +load method
+ (void)load
{
    swizzleMethod([self class].@selector(viewDidAppear:), @selector(swizzled_viewDidAppear:));
}

- (void)swizzled_viewDidAppear:(BOOL)animated
{
    // Note that the method has been swapped
    // swizzled_viewDidAppear calls the system's viewDidAppear
    [self swizzled_viewDidAppear:animated];
     // Logging
    NSLog(@"% @".NSStringFromClass([self class]));
}

// Switch methods
void swizzleMethod(Class class.SEL originalSelector, SEL swizzledSelector)
{
    // Get the original method and the method to swap
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
    // Determine whether the method is implemented
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    
    // Add a method if there is no implementation
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector,          method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    else {
        // Switch the two methodsmethod_exchangeImplementations(originalMethod, swizzledMethod); }}Copy the code
5. The realization of the KVO

You can refer to the previous article, including how the system implements the KVO principle and use the Runtime related API to implement a KVO to explore OC object, ISA pointer and the implementation principle of KVO

6. Use the message forwarding mechanism to solve the problem that the method cannot be found

Refer to the previous article for detailed analysis and relevant methods to achieve message forwarding. Runtime Summary (II)

3

  • I have seen many questions on the Internet, mainly around the isa structure of the runtime, the structure of the class, source code, message forwarding, application scenarios, the following isa few common examples and personal feelings of a good interview questions to share.

1. Talk about the messaging mechanism of the Runtime

2. What is the process of message forwarding mechanism

3. What is Runtime? Has it ever been used in a project?

Interview questions (with answers)