In project code writing, it is common to meet the requirements of third-party frameworks or native methods or when a Method is called a lot in the project, we want to replace or modify it in batches. In order to avoid changing the original function, we need to add additional functions on the basis of maintaining the original function, so we need to use Swizzle Method

Method replaces Swizzle Method in order to replace the implementation of the Method. Usually when you implement a method, you use @selector, and the way to implement a method is to take a method number (pointer to the method) in @selector, represented by SEL type, and it points to an IMP(pointer to the method implementation), and the method is to replace the IMP, so that the method replacement

Instance method substitution

#import <objc/runtime.h>

+ (void)swizzleInstanceMethodWithOriginalSEL:(SEL)originalSel SwizzleNewSEL: (SEL)newSel {
    
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    if (!originalMethod || !newMethod) {
        return;
    }
    // Add a layer of protection, if added successfully, this method does not exist in this class, but in the parent class, cannot exchange the method, otherwise the object of the parent class call this method crash; If the method fails to be added, the method exists in the class
    BOOL addMethod = class_addMethod(self, originalSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (addMethod) {
        SwizzledMethod (swizzledMethod, swizzledMethod, swizzledMethod, swizzledMethod
        class_replaceMethod(self, newSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }else{ method_exchangeImplementations(originalMethod, newMethod); }}Copy the code

Class method substitution

#import <objc/runtime.h>

+ (void)swizzleClassMethodWithOriginalSEL:(SEL)originalSel SwizzleNewSEL: (SEL)newSel {
    Class class = object_getClass(self);
    Method originalMethod = class_getInstanceMethod(class.originalSel);
    Method newMethod = class_getInstanceMethod(class.newSel);
    if (!originalMethod || !newMethod) {
        return;
    }
    
    BOOL addMethod = class_addMethod(class, originalSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (addMethod) {
        class_replaceMethod(class, newSel, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }else{ method_exchangeImplementations(originalMethod, newMethod); }}Copy the code

The use of alternative methods

+ (void)load{
    // Load does not need to call super for method substitution, otherwise it will cause the parent class to be Swizzling repeatedly
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{// Ensure that method substitution is performed only once

        [self swizzleInstanceMethodWithClass:NSClassFromString(@"__NSArrayI") OriginalSEL:@selector(objectAtIndex:) SwizzleNewSEL:@selector(safe_objectAtIndex:)];
    });
}
Copy the code

  • To ensure that the Swizzle Method replacement is always called, it needs to be done in load (which is called when the class file is loaded (before the program starts))

  • To avoid subclasses or subclasses of subclasses calling [super Load], Swizzling is executed multiple times (equivalent to SEL and IMP being swapped multiple times). This causes the first execution to swap successfully, the second execution to swap back, and the third execution….. You need to use the GCD method dispatch_once in load to ensure that the method is executed only once and you don’t need to call the [super load] method

Use method substitution to handle crashes caused by out-of-bounds array, out-of-bounds string subscript, and null dictionary assignment _Demo