Runtime的用法有哪几种
1, 动态添加一个类, 就像KVO一样, 系统是在程序运行的时候根据你要监听的类, 动态添加一个新类继承自该类, 然后重写原类的setter方法并在里面通知observer的.
// 创建一个类(size_t extraBytes该参数通常指定为0, 该参数是分配给类和元类对象尾部的索引ivars的字节数。) Class clazz = objc_allocateClassPair([NSObject class], "GoodPerson", 0); // 添加ivar // @encode(aType) : 返回该类型的C字符串 class_addIvar(clazz, "_name", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *)); class_addIvar(clazz, "_age", sizeof(NSUInteger), log2(sizeof(NSUInteger)), @encode(NSUInteger)); // 注册该类 objc_registerClassPair(clazz); // 创建实例对象 id object = [[clazz alloc] init]; // 设置ivar [object setValue:@"Tracy" forKey:@"name"]; Ivar ageIvar = class_getInstanceVariable(clazz, "_age"); object_setIvar(object, ageIvar, @18); // 打印对象的类和内存地址 NSLog(@"%@", object); // 打印对象的属性值 NSLog(@"name = %@, age = %@", [object valueForKey:@"name"], object_getIvar(object, ageIvar)); // 当类或者它的子类的实例还存在,则不能调用objc_disposeClassPair方法 object = nil; // 销毁类 objc_disposeClassPair(clazz); ---------------------
2,
通过runtime获取一个类的所有属性
Person *p = [[Person alloc] init]; [p setValue:@"Kobe" forKey:@"name"]; [p setValue:@18 forKey:@"age"]; // p.address = @"广州大学城"; p.weight = 110.0f; // 1.打印所有ivars unsigned int ivarCount = 0; // 用一个字典装ivarName和value NSMutableDictionary *ivarDict = [NSMutableDictionary dictionary]; Ivar *ivarList = class_copyIvarList([p class], &ivarCount); for(int i = 0; i < ivarCount; i++){ NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivarList[i])]; id value = [p valueForKey:ivarName]; if (value) { ivarDict[ivarName] = value; } else { ivarDict[ivarName] = @"值为nil"; } } // 打印ivar for (NSString *ivarName in ivarDict.allKeys) { NSLog(@"ivarName:%@, ivarValue:%@",ivarName, ivarDict[ivarName]); } // 2.打印所有properties unsigned int propertyCount = 0; // 用一个字典装propertyName和value NSMutableDictionary *propertyDict = [NSMutableDictionary dictionary]; objc_property_t *propertyList = class_copyPropertyList([p class], &propertyCount); for(int j = 0; j < propertyCount; j++){ NSString *propertyName = [NSString stringWithUTF8String:property_getName(propertyList[j])]; id value = [p valueForKey:propertyName]; if (value) { propertyDict[propertyName] = value; } else { propertyDict[propertyName] = @"值为nil"; } } // 打印property for (NSString *propertyName in propertyDict.allKeys) { NSLog(@"propertyName:%@, propertyValue:%@",propertyName, propertyDict[propertyName]); } // 3.打印所有methods unsigned int methodCount = 0; // 用一个字典装methodName和arguments NSMutableDictionary *methodDict = [NSMutableDictionary dictionary]; Method *methodList = class_copyMethodList([p class], &methodCount); for(int k = 0; k < methodCount; k++){ SEL methodSel = method_getName(methodList[k]); NSString *methodName = [NSString stringWithUTF8String:sel_getName(methodSel)]; unsigned int argumentNums = method_getNumberOfArguments(methodList[k]); methodDict[methodName] = @(argumentNums - 2); // -2的原因是每个方法内部都有self 和 selector 两个参数 } // 打印method for (NSString *methodName in methodDict.allKeys) { NSLog(@"methodName:%@, argumentsCount:%@", methodName, methodDict[methodName]); } ---------------------
3,
动态变量控制,动态修改变量的值
-(void)changeAge{ unsigned int count = 0; //动态获取XiaoMing类中的所有属性[当然包括私有] Ivar *ivar = class_copyIvarList([self.xiaoMing class], &count); //遍历属性找到对应age字段 for (int i = 0; i<count; i++) { Ivar var = ivar[i]; const char *varName = ivar_getName(var); NSString *name = [NSString stringWithUTF8String:varName]; if ([name isEqualToString:@"_age"]) { //修改对应的字段值成20 object_setIvar(self.xiaoMing, var, @"20"); break; } } NSLog(@"XiaoMing's age is %@",self.xiaoMing.age); }
4,
在NSObject的分类中增加方法来避免使用KVC赋值的时候出现崩溃
在有些时候我们需要通过KVC去修改某个类的私有变量,但是又不知道该属性是否存在,如果类中不存在该属性,那么通过KVC赋值就会crash,这时也可以通过运行时进行判断。同样我们在NSObject的分类中增加如下方法。
#import "NSObject+objc.h" #import <objc/runtime.h> @implementation NSObject (objc) -(BOOL)hasProperty:(NSString *)property { BOOL flag = NO; u_int count = 0; Ivar *ivars = class_copyIvarList([self class], &count); for (int i = 0; i < count; i++) { const char *propertyName = ivar_getName(ivars[i]); NSString *propertyString = [NSString stringWithUTF8String:propertyName]; if ([propertyString isEqualToString:property]){ flag = YES; } } return flag; } @end
5,
利用runtime的动态交换方法实现
+(void)run { NSLog(@"Person Run....."); } +(void)study { NSLog(@"Person study....."); }
// 获取两个类的类方法 Method m1 = class_getClassMethod([Person class], @selector(run)); Method m2 = class_getClassMethod([Person class], @selector(study)); // 开始交换方法实现 method_exchangeImplementations(m1, m2);
[Person run];
[Person study];
6,
动态添加方法
// void(*)() // 默认方法都有两个隐式参数, void eat(id self,SEL sel) { NSLog(@"%@ %@",self,NSStringFromSelector(sel)); } // 当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来. // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法 + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(eat)) { // 动态添加eat方法 // 第一个参数:给哪个类添加方法 // 第二个参数:添加方法的方法编号 // 第三个参数:添加方法的函数实现(函数地址) // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd class_addMethod(self, @selector(eat), eat, "v@:"); } return [super resolveInstanceMethod:sel]; }