一、Runtime API
1、类
1、动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
2、注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls)
3、销毁一个类
void objc_disposeClassPair(Class cls)
4、设置和获取isa指向的Class
Class object_setClass(id obj, Class cls) Class object_getClass(id obj)
5、判断一个OC对象是否为类和元类
BOOL object_isClass(id obj) BOOL class_isMetaClass(Class cls)
6、获取父类
Class class_getSuperclass(Class cls)
2、成员变量
1、获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)
2、拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
3、动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)
4、获取成员变量的相关信息
const char *ivar_getName(Ivar v) const char *ivar_getTypeEncoding(Ivar v)
5、设置成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
6、获取成员变量的对象
id object_getIvar(id obj, Ivar ivar)
3、属性
1、获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)
2、拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
3、动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
4、动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes, unsigned int attributeCount)
5、获取属性的一些信息
const char *property_getName(objc_property_t property) const char *property_getAttributes(objc_property_t property)
4、方法
1、获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name) Method class_getClassMethod(Class cls, SEL name)
2、方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name) IMP method_setImplementation(Method m, IMP imp) void method_exchangeImplementations(Method m1, Method m2)
3、拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)
4、动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
5、动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
6、获取方法的相关信息(带有copy的需要调用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)
6、选择器相关
const char *sel_getName(SEL sel) SEL sel_registerName(const char *str)
7、用block作为方法实现
IMP imp_implementationWithBlock(id block) id imp_getBlock(IMP anImp) BOOL imp_removeBlock(IMP anImp)
二、示例
1、动态创建类、添加成员变量、添加方法
#import <Foundation/Foundation.h> void run(id self, SEL _cmd) { NSLog(@"_____ %@ - %@", self, NSStringFromSelector(_cmd)); } int main(int argc, const char * argv[]) { @autoreleasepool { // 1、创建类【类型可动态赋值】 NSString *className = [NSString stringWithFormat:@"ZMDog"]; Class newClass = objc_allocateClassPair([NSObject class], className, 0); // 添加成员变量 class_addIvar(newClass, "_age", 4, 1, @encode(int)); class_addIvar(newClass, "_weight", 8, 1, @encode(int)); // 添加方法 class_addMethod(newClass, @selector(run), (IMP)run, "v@:"); // 2、注册类 objc_registerClassPair(newClass); { // 初始化 id dog = [[newClass alloc] init]; // 赋值 [dog setValue:@10 forKey:@"_age"]; [dog setValue:@20 forKey:@"_weight"]; // 方法实现 [dog run]; // 打印结果 NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]); } // 3、释放类 objc_disposeClassPair(newClass); } return 0; }
注意:
1、成员变量一定要在注册类之前实现
2、方法objc_disposeClassPair,如果这个类或子类的实例存在,不要调用。
2、设置isa指向的类
ZMPerson.m:
#import "ZMPerson.h" @implementation ZMPerson - (void)run { NSLog(@"%s", __func__); } @end
ZMCar.m:
#import "ZMCar.h" @implementation ZMCar - (void)run { NSLog(@"%s", __func__); } @end
实现:
int main(int argc, const char * argv[]) { @autoreleasepool { ZMPerson *person = [[ZMPerson alloc] init]; [person run]; object_setClass(person, [ZMCar class]); [person run]; } return 0; }
打印结果:
[29582:563758] -[ZMPerson run] [29582:563758] -[ZMCar run]
3、判断类和元类
int main(int argc, const char * argv[]) { @autoreleasepool { ZMPerson *person = [[ZMPerson alloc] init]; NSLog(@"%d %d %d", object_isClass(person), object_isClass([ZMPerson class]), object_isClass(object_getClass([ZMPerson class])) ); } return 0; }
打印结果:
[28681:543229] 0 1 1
4、成员变量【获取成员变量信息、成员变量赋值和取值、拷贝成员变量列表】
int main(int argc, const char * argv[]) { @autoreleasepool { // 1、获取成员变量信息 Ivar ageIvar = class_getInstanceVariable([ZMPerson class], "_age"); NSLog(@"ageIvar:%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar)); Ivar nameIvar = class_getInstanceVariable([ZMPerson class], "_name"); ZMPerson *person = [[ZMPerson alloc] init]; // 2、设置成员变量的值 object_setIvar(person, nameIvar, @"123"); object_setIvar(person, ageIvar, (__bridge id)(void *)10); // 3、获取成员变量的值 NSLog(@"person.name:%@", person.name); NSLog(@"person.age:%d",person.age); NSLog(@"=================="); // 成员变量的数量 unsigned int count; // 拷贝成员变量数组 Ivar *ivars = class_copyIvarList([ZMPerson class], &count); for (int i = 0; i < count; i++) { // 取出i位置的成员变量 Ivar ivar = ivars[i]; NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar)); } // 释放成员变量 free(ivars); } return 0; }
self.textF.placeholder = @"请填写"; // 直接修改私有成员变量,在iOS13以后被禁止 // [self.textF setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"]; // ================= // 要改成如下方式实现: // 获取成员变量 Ivar placeholderIvar = class_getInstanceVariable([UITextField class], "_placeholderLabel"); UILabel *placeholderLabel = object_getIvar(self.textF, placeholderIvar); //给变量赋值 placeholderLabel.textColor = [UIColor redColor];
5、数模转换
NSObject+Json.h: #import <Foundation/Foundation.h> @interface NSObject (Json) + (instancetype)zm_objectWithJson:(NSDictionary *)json; @end
NSObject+Json.m:
#import "NSObject+Json.h" #import <objc/runtime.h> @implementation NSObject (Json) + (instancetype)zm_objectWithJson:(NSDictionary *)json { id obj = [[self alloc] init]; unsigned int count; // 拷贝示例变量列表 Ivar *ivars = class_copyIvarList(self, &count); for (int i = 0; i < count; i++) { // 取出i位置的成员变量 Ivar ivar = ivars[i]; // 获取成员变量名称 NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)]; // 移除成员变量首个下划线【_】 [name deleteCharactersInRange:NSMakeRange(0, 1)]; // 设值 id value = json[name]; if ([name isEqualToString:@"ID"]) { value = json[@"id"]; } // 通过KVC赋值 [obj setValue:value forKey:name]; } // 释放变量 free(ivars); return obj; } @end
ZMPerson.h:
#import <Foundation/Foundation.h> @interface ZMPerson : NSObject @property (assign, nonatomic) int ID; @property (assign, nonatomic) int weight; @property (assign, nonatomic) int age; @property (copy, nonatomic) NSString *name; - (void)run; @end
ZMPerson.m:
#import "ZMPerson.h" @implementation ZMPerson - (void)run { NSLog(@"%s", __func__); } @end
【main.m】实现:
#import <Foundation/Foundation.h> #import "ZMPerson.h" #import "NSObject+Json.h" int main(int argc, const char * argv[]) { @autoreleasepool { // 字典转模型 NSDictionary *json = @{ @"id" : @10, @"age" : @20, @"weight" : @80, @"name" : @"Tony" }; ZMPerson *person = [ZMPerson zm_objectWithJson:json]; } return 0; }
6、方法交换
ZMPerson.h:
#import <Foundation/Foundation.h> @interface ZMPerson : NSObject - (void)run; - (void)test; @end
#import "ZMPerson.h" @implementation ZMPerson - (void)run { NSLog(@"%s", __func__); } - (void)test { NSLog(@"%s", __func__); } @end
main.m 实现:
#import <Foundation/Foundation.h> #import <objc/runtime.h> #import "ZMPerson.h" int main(int argc, const char * argv[]) { @autoreleasepool { ZMPerson *person = [[ZMPerson alloc] init]; Method runMethod = class_getInstanceMethod([ZMPerson class], @selector(run)); Method testMethod = class_getInstanceMethod([ZMPerson class], @selector(test)); method_exchangeImplementations(runMethod, testMethod); [person run]; } return 0; }
打印结果:
// 代码执行的是[person run];,结果实际执行到了test中 [31079:590617] -[MJPerson test]
7、方法替代
#import <Foundation/Foundation.h> #import <objc/runtime.h> void myrun() { NSLog(@"---myrun"); } int main(int argc, const char * argv[]) { @autoreleasepool { MJPerson *person = [[MJPerson alloc] init]; class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v"); class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{ NSLog(@"123123"); }), "v"); [person run]; } return 0; }