Runtime dynamically creates a class and modifies the operation of the method

Keywords: iOS

As we all know, Objective-C is a dynamic language, and its dynamics is embodied in runtime on the one hand.

Next, let's take a look at creating a class dynamically through runtime, adding attributes and methods to the class, and also modifying and replacing the implementation of existing methods.

The steps to create a class dynamically:
  1. Create a class
  2. Add attributes (must be added before registration)
  3. Register this class
  4. Adding method
  5. Destroy this class
-(void)createClass
{
    //Create a new class MyClass
    Class myClass = objc_allocateClassPair([NSObject class], "MyClass", 0);
    //Add ivar
    //@ encode(aType): Returns a C string of this type
    class_addIvar(myClass, "_address", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
    class_addIvar(myClass, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger));
    //Registration class
    objc_registerClassPair(myClass);
    //Create examples
    id object = [[myClass alloc] init];
    //Set values for ivar
    [object setValue:@"china" forKey:@"address"];
    [object setValue:@20 forKey:@"age"];
    NSLog(@"address=%@,age=%@",[object valueForKey:@"address"],[object valueForKey:@"age"]);
    
    //Adding method
    Class class = [self class];
    SEL selfInsMethod1 = @selector(instanceMethod1);
    Method insMethod1 = class_getInstanceMethod(class, selfInsMethod1);
    IMP impInsMethod1 = method_getImplementation(insMethod1);
    const char *typeInsMethod1 = method_getTypeEncoding(insMethod1);
    SEL selfNewInsMethod = @selector(newInsMethod);
    BOOL isInsAdded = class_addMethod(myClass, selfNewInsMethod, impInsMethod1, typeInsMethod1);
    if (!isInsAdded) {
        NSLog(@"New instance method addition failed!");
    }
    [object performSelector:selfNewInsMethod];
    
    //When an instance of a class or its subclass still exists, the objc_disposeClassPair method cannot be invoked.
    object = nil;
    //Destruction
    objc_disposeClassPair(myClass);
    
}

-(void)instanceMethod1
{
    NSLog(@"%s",__func__);
}

-(void)instanceMethod2
{
    NSLog(@"%s",__func__);
}

+ (void)classMethod1
{
    NSLog(@"%s",__func__);
}

+ (void)classMethod2
{
    NSLog(@"%s",__func__);
}
Method Swizzling

Method Swizzling is a black magic developed by iOS. It can operate various methods flexibly.

  • The + (void)load is called only when the class is first loaded, and it is called only once.
  • Only swizzling in + load is safe.
+ (void)load
{
    NSLog(@"%s",__func__);

    //1. Get the current class name
    Class class = [self class];
    //2. Getting method name (selector)
    SEL selfInsMethod1 = @selector(instanceMethod1);
    SEL selfInsMethod2 = @selector(instanceMethod2);
    SEL selfClassMethod1 = @selector(classMethod1);
    SEL selfClassMethod2 = @selector(classMethod2);
    //3. Obtaining Method Objects by Method Name
    Method insMethod1 = class_getInstanceMethod(class, selfInsMethod1);
    Method insMethod2 = class_getInstanceMethod(class, selfInsMethod2);
    Method classMethod1 = class_getClassMethod(class, selfClassMethod1);
    Method classMethod2 = class_getClassMethod(class, selfClassMethod2);
    
    //4. Implementation of Exchange Example Method
    if (!insMethod1 || !insMethod2) {
        NSLog(@"Instance method failed to implement runtime exchange!");
        return;
    }
    method_exchangeImplementations(insMethod1, insMethod2);
    //5. Implementation of Exchange Class Method
    if (!classMethod1 || !classMethod2) {
        NSLog(@"Class method failed to implement runtime exchange!");
        return;
    }
    method_exchangeImplementations(classMethod1, classMethod2);
    //Class and instance methods can also be exchanged, provided there are no code errors
 
    /* Implementation of methods in reset classes */
    //Implementation of Acquisition Method
    IMP impInsMethod1 = method_getImplementation(insMethod1);
    IMP impInsMethod2 = method_getImplementation(insMethod2);
    IMP impClassMethod1 = method_getImplementation(classMethod1);
    IMP impClassMethod2 = method_getImplementation(classMethod2);
    //Realization of Reset Method
    method_setImplementation(insMethod1, impInsMethod2);
    method_setImplementation(insMethod2, impInsMethod1);
    method_setImplementation(classMethod1, impClassMethod2);
    method_setImplementation(classMethod2, impClassMethod1);
    
    //Get method code type
    const char *typeInsMethod1 = method_getTypeEncoding(insMethod1);
    const char *typeInsMethod2 = method_getTypeEncoding(insMethod2);
    const char *typeClassMethod1 = method_getTypeEncoding(insMethod1);
    const char *typeClassMethod2 = method_getTypeEncoding(insMethod2);
    //Replacement Instance Method
    class_replaceMethod(class, selfInsMethod1, impInsMethod2, typeInsMethod2);
    class_replaceMethod(class, selfInsMethod2, impInsMethod1, typeInsMethod1);
    //Trying to replace the implementation of the class method, the actual result is invalid, and class_replaceMethod can only replace the implementation of the instance method.
    class_replaceMethod(class, selfClassMethod1, impClassMethod2, typeClassMethod2);
    class_replaceMethod(class, selfClassMethod2, impClassMethod1, typeClassMethod1);

    
    /* Adding new instance methods and class methods to classes */
    SEL selfNewInsMethod = @selector(newInsMethod);
    BOOL isInsAdded = class_addMethod(class, selfNewInsMethod, impInsMethod1, typeInsMethod1);
    if (!isInsAdded) {
        NSLog(@"New instance method addition failed!");
    }
}

Posted by kevin1 on Tue, 01 Oct 2019 18:43:29 -0700