没怎么用过运行时,做毕设的时候拿来改了一下textfield的光标颜色,了解实在有限。现在来了解一下除简单获取私有变量的其它综合用途。
Runtime简介
按国际惯例,背书
- 运行时是一种面向对象的编程语言环境,类似于java的虚拟机
- OC最主要的特点就是在程序运行的时候,以发送消息的方式调用方法
- 运行时是OC的核心、底层,OC就是基于运行时的
- 日常工作中,主要应用场景是关联对象、可以给分类动态的添加属性;动态的获取类的属性,用于字典转模型
- 交叉方法,在无法修改系统的或者第三方框架的方式时,利用交叉方法,先执行自己的方法,在交换执行三方框架的方法,
具体功能实现
动态获取类的属性列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| + (NSArray *)xd_objPropertyArr {
unsigned int count = 0; objc_property_t *proArr = class_copyPropertyList([self class], &count); NSLog(@"属性的数量%d",count); NSMutableArray *MArr = [NSMutableArray array]; for (unsigned int i = 0; i < count; i++) { objc_property_t pty = proArr[i]; const char *cName = property_getName(pty); NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding]; [MArr addObject:name]; } free(proArr); return MArr.copy; }
|
字典转模型
模型初始化的时候可以用,如果返回的json很大,不是所有模型都需要这么多的元素,这时候下面的方法就派上用场了,当然还有很多其它的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| + (instancetype)xd_objWithDic:(NSDictionary *)dic { id object = [[self alloc] init]; NSArray *proArr = [self xd_objPropertyArr]; [dic enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) { NSLog(@"key %@, value %@",key,obj); if ([proArr containsObject:key]) { [object setValue:obj forKey:key]; } }]; return object; }
|
归档
如果需要归档的属性很多,写initWithCoder:和encoderWithCoder:这两个方法会很枯燥,那么利用Runtime也能较好地这个问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| @implementation Person
- (id)initWithCoder:(NSCoder *)coder { unsigned int iVarCount = 0; Ivar *iVarList = class_copyIvarList([self class], &iVarCount); for (int i = 0; i < iVarCount; i++) { Ivar var = *(iVarList + i); const char * varName = ivar_getName(var); NSString *key = [NSString stringWithUTF8String:varName]; id value = [coder decodeObjectForKey:key]; if (value) { [self setValue:value forKey:key]; } } free(iVarList); return self; } - (void)encodeWithCoder:(NSCoder *)coder { unsigned int varCount = 0; Ivar *ivarList = class_copyIvarList([self class], &varCount); for (int i = 0; i < varCount; i++) { Ivar var = *(ivarList + i); const char *varName = ivar_getName(var); NSString *key = [NSString stringWithUTF8String:varName]; id varValue = [self valueForKey:key]; if (varValue) { [coder encodeObject:varValue forKey:key]; } } free(ivarList); }
|
交叉?混合?替换?方法
这是我目前发现Runtime最厉害的地方,网上说这是什么黑魔法?利用这特性可以很方便给原有的方法加入、修改、替换、混合业务逻辑而不修改原来的类文件。
在分类中进行编辑
1 2 3 4 5 6 7 8 9 10 11
| + (void)load { Method originalMethod = class_getInstanceMethod([self class], @selector(viewWillAppear:)); Method swizzledMethod = class_getInstanceMethod([self class], @selector(xd_viewWillAppear:)); method_exchangeImplementations(originalMethod, swizzledMethod); } - (void)xd_viewWillAppear:(BOOL)animated { NSLog(@"搞事"); [self xd_viewWillAppear:YES]; }
|
后面会继续学习埋点和归档的优化