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:
- Create a class
- Add attributes (must be added before registration)
- Register this class
- Adding method
- 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!"); } }