总结起来,iOS中的RunTime的做用有如下几点:数组
1.发送消息(obj_msgSend)缓存
2.方法交换(method_exchangeImplementations)app
3.消息转发ide
4.动态添加方法函数
5.给分类添加属性测试
6.获取到类的成员变量及其方法优化
7.动态添加类编码
8.解档与归档atom
9.字典转模型spa
runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了不少底层的C语言API。
在咱们平时编写的OC代码中, 程序运行过程时, 其实最终都是转成了runtime的C语言代码, runtime算是OC的幕后工做者.例如[target doSomething];
会被转化成objc_msgSend(target, @selector(doSomething));
。
OC中一切都被设计成了对象,咱们都知道一个类被初始化成一个实例,这个实例是一个对象。实际上一个类本质上也是一个对象,在runtime中用结构体表示。
例如: OC就是典型的运行时机制,OC属于动态调用过程,在编译的时候并不能决定真正调用哪一个函数,只有在真正运行时才会根据函数的名称找到对应的函数来调用.而C语言中函数在编译的时候就会决定调用哪一个函数.
相关的定义:
1 /// 描述类中的一个方法 2 typedef struct objc_method *Method; 3 4 /// 实例变量 5 typedef struct objc_ivar *Ivar; 6 7 /// 类别Category 8 typedef struct objc_category *Category; 9 10 /// 类中声明的属性 11 typedef struct objc_property *objc_property_t;
类在runtime中的表示
1 //类在runtime中的表示 2 struct objc_class { 3 Class isa;//指针,顾名思义,表示是一个什么, 4 //实例的isa指向类对象,类对象的isa指向元类 5 6 #if !__OBJC2__ 7 Class super_class; //指向父类 8 const char *name; //类名 9 long version; 10 long info; 11 long instance_size 12 struct objc_ivar_list *ivars //成员变量列表 13 struct objc_method_list **methodLists; //方法列表 14 struct objc_cache *cache;//缓存 15 //一种优化,调用过的方法存入缓存列表,下次调用先找缓存 16 struct objc_protocol_list *protocols //协议列表 17 #endif 18 } OBJC2_UNAVAILABLE; 19 /* Use `Class` instead of `struct objc_class *` */
有时候会有这样的需求,咱们须要知道当前类中每一个属性的名字(好比字典转模型,字典的Key和模型对象的属性名字不匹配)。
咱们能够经过runtime的一系列方法获取类的一些信息(包括属性列表,方法列表,成员变量列表,和遵循的协议列表)。
1 // 2 // RunTimeTool.m 3 // IOS_0423_RunTime 4 // 5 // Created by ma c on 16/4/23. 6 // Copyright © 2016年 博文科技. All rights reserved. 7 // 8 9 #import "RunTimeTool.h" 10 #import <objc/runtime.h> 11 #import "Person.h" 12 13 @implementation RunTimeTool 14 15 //得到成员变量 16 + (void)accessToMemberVariable 17 { 18 unsigned int count; 19 //得到成员变量结构体 20 Ivar *ivars = class_copyIvarList([Person class], &count); 21 for (int i = 0; i < count; i++) { 22 Ivar ivar = ivars[i]; 23 24 //根据Ivar得到成员变量的名称 25 const char *nameC = ivar_getName(ivar); 26 //C的字符串转成OC字符串 27 NSString *nameOC = [NSString stringWithUTF8String:nameC]; 28 NSLog(@"%@",nameOC); 29 } 30 free(ivars); 31 } 32 //得到属性 33 + (void)accessToProperty 34 { 35 unsigned int count; 36 //得到指向该类全部属性的指针 37 objc_property_t *properties = class_copyPropertyList([Person class], &count); 38 39 for (int i = 0; i < count; i++) { 40 //得到该类一个属性的指针 41 objc_property_t property = properties[i]; 42 43 //得到属性的名称 44 const char *nameC = property_getName(property); 45 //C的字符串转成OC字符串 46 NSString *nameOC = [NSString stringWithUTF8String:nameC]; 47 NSLog(@"%@",nameOC); 48 } 49 free(properties); 50 } 51 //得到方法 52 + (void)accessToMethod 53 { 54 unsigned int count; 55 //得到指向该类全部方法的指针 56 Method *methods = class_copyMethodList([Person class], &count); 57 58 for (int i = 0; i < count; i++) { 59 60 //得到该类的一个方法指针 61 Method method = methods[i]; 62 //获取方法 63 SEL methodSEL = method_getName(method); 64 //将方法名转化成字符串 65 const char *methodC = sel_getName(methodSEL); 66 //C的字符串转成OC字符串 67 NSString *methodOC = [NSString stringWithUTF8String:methodC]; 68 //得到方法参数个数 69 int arguments = method_getNumberOfArguments(method); 70 NSLog(@"%@方法的参数个数:%d",methodOC, arguments); 71 } 72 free(methods); 73 } 74 //得到协议 75 + (void)accessToProtocol 76 { 77 unsigned int count; 78 //获取指向该类遵循的全部协议的指针 79 __unsafe_unretained Protocol **protocols = class_copyProtocolList([Person class], &count); 80 81 for (int i = 0; i < count; i++) { 82 //获取指向该类遵循的一个协议的指针 83 Protocol *protocol = protocols[i]; 84 85 //得到属性的名称 86 const char *nameC = protocol_getName(protocol); 87 //C的字符串转成OC字符串 88 NSString *nameOC = [NSString stringWithUTF8String:nameC]; 89 NSLog(@"%@",nameOC); 90 91 } 92 free(protocols); 93 } 94 95 96 @end
objc_msgSend,只有对象才能发送消息,所以以objc开头.
使用消息机制的前提:导入#improt<objc/message.h>
1 // 建立person对象 2 Person *p = [[Person alloc] init]; 3 4 // 调用对象方法 5 [p eat]; 6 7 // 本质:让对象发送消息 8 objc_msgSend(p, @selector(eat)); 9 10 // 调用类方法的方式:两种 11 // 第一种经过类名调用 12 [Person eat]; 13 // 第二种经过类对象调用 14 [[Person class] eat]; 15 16 // 用类名调用类方法,底层会自动把类名转换成类对象调用 17 // 本质:让类对象发送消息 18 objc_msgSend([Person class], @selector(eat));
消息机制原理:对象根据方法编号(SEL)去映射表查找对应的方法实现
对象在收到没法解读的消息后,首先会调用所属类的 + (BOOL)resolveInstanceMethod:(SEL)sel
这个方法在运行时,没有找到SEL的IML时就会执行。这个函数是给类利用class_addMethod添加函数的机会。根据文档,若是实现了添加函数代码则返回YES,未实现返回NO。
首先从外部隐式调用一个不存在的方法:
[person performSelector:@selector(sleep:) withObject:@"8小时"];
而后,在person对象内部重写拦截调用的方法,动态添加方法。
1 //动态添加方法 2 + (BOOL)resolveInstanceMethod:(SEL)sel 3 { 4 if ([NSStringFromSelector(sel) isEqualToString:@"sleep:"]) { 5 class_addMethod(self, sel, (IMP)sleepMethod, "v@:*"); 6 return YES; 7 } 8 return [super resolveInstanceMethod:sel]; 9 } 10 void sleepMethod(id self, SEL _cmd, NSString *string) 11 { 12 NSLog(@"睡了%@",string); 13 }
其中class_addMethod
的四个参数分别是:
1.Class cls
给哪一个类添加方法,本例中是self
2.SEL name
添加的方法,本例中是重写的拦截调用传进来的selector。
3.IMP imp
方法的实现,C方法的方法实现能够直接得到。若是是OC方法,能够用+ (IMP)instanceMethodForSelector:(SEL)aSelector;
得到方法的实现。
4."v@:" 意思是,v表明无返回值void,若是是i则表明int;@表明 id sel; : 表明 SEL _cmd;
“v@:@@” 意思是,两个参数的没有返回值。
"v@:*"意思是,表明有一个参数的方法
若是在+ (BOOL)resolveInstanceMethod:(SEL)sel中没有找到或者添加方法
消息继续往下传递到- (id)forwardingTargetForSelector:(SEL)aSelector看看是否是有对象能够执行这个方法
1 + (BOOL)resolveInstanceMethod:(SEL)sel { 2 3 return [super resolveInstanceMethod:sel]; 4 } 5 6 7 //消息转发 8 - (id)forwardingTargetForSelector:(SEL)aSelector 9 { 10 Class class = NSClassFromString(@"Chinese"); 11 Person *person = [[class alloc] init]; 12 if (aSelector == NSSelectorFromString(@"study")) { 13 return person; 14 } 15 return [super forwardingTargetForSelector:aSelector]; 16 }
1 //动态交换方法 2 + (void)load 3 { 4 /* 5 load方法会在类第一次加载时调用 6 交换方法应该保证,在程序中只被执行一次 7 */ 8 9 SEL runSEL = @selector(run); 10 SEL eatSEL = @selector(eat); 11 12 //两个方法的Method方法地址 13 Method runMethod = class_getInstanceMethod([self class], runSEL); 14 Method eatMethod = class_getInstanceMethod([self class], eatSEL); 15 16 //首先动态的添加方法,实现是被交换的方法,返回值表示添加成功仍是失败 17 BOOL isAdd = class_addMethod(self, eatSEL, method_getImplementation(runMethod), "v@:"); 18 19 if (isAdd) { 20 //若是成功说明类中不存在这个方法实现,将被交换的方法实现替换这个并不存在的实现 21 class_replaceMethod(self, runSEL, method_getImplementation(eatMethod), "v@:"); 22 } else { 23 method_exchangeImplementations(runMethod, eatMethod); 24 } 25 }
1 #import "Person.h" 2 3 @interface Person (Ext) 4 5 @property (nonatomic, strong) NSString *IDCard; 6 7 @end 8 9 10 // 11 // Person+Ext.m 12 // IOS_0423_RunTime 13 // 14 // Created by ma c on 16/4/24. 15 // Copyright © 2016年 博文科技. All rights reserved. 16 // 17 18 #import "Person+Ext.h" 19 #import <objc/message.h> 20 21 @implementation Person (Ext) 22 23 static const char *key = "identifier"; 24 25 - (void)setIDCard:(NSString *)IDCard 26 { 27 // 第一个参数:给哪一个对象添加关联 28 // 第二个参数:关联的key,经过这个key获取 29 // 第三个参数:关联的value 30 // 第四个参数:关联的策略 31 objc_setAssociatedObject(self, key, IDCard, OBJC_ASSOCIATION_COPY_NONATOMIC); 32 33 } 34 35 - (NSString *)IDCard 36 { 37 // 根据关联的key,获取关联的值。 38 return objc_getAssociatedObject(self, key); 39 } 40 41 @end
1 #import "AppDelegate.h" 2 #import <objc/runtime.h> 3 4 @interface AppDelegate () 5 6 @end 7 8 @implementation AppDelegate 9 10 11 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 12 // 延时,等待全部控件加载完 13 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 14 [self test]; 15 }); 16 return YES; 17 } 18 19 - (void)test 20 { 21 // 这个规则确定事先跟服务端沟通好,跳转对应的界面须要对应的参数 22 NSDictionary *userInfo = @{ 23 @"class": @"BowenViewController", 24 @"property": @{ 25 @"ID": @"123", 26 @"type": @"12" 27 } 28 }; 29 30 [self push:userInfo]; 31 } 32 33 #pragma mark 接收推送消息 34 - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo 35 { 36 37 [self push:userInfo]; 38 } 39 40 /** 41 * 跳转界面 42 */ 43 - (void)push:(NSDictionary *)params 44 { 45 // 类名 46 NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]]; 47 const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding]; 48 49 // 从一个字串返回一个类 50 Class newClass = objc_getClass(className); 51 if (!newClass) 52 { 53 // 建立一个类 54 Class superClass = [NSObject class]; 55 newClass = objc_allocateClassPair(superClass, className, 0); 56 // 注册你建立的这个类 57 objc_registerClassPair(newClass); 58 } 59 // 建立对象 60 id instance = [[newClass alloc] init]; 61 62 NSDictionary *propertys = params[@"property"]; 63 [propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { 64 // 检测这个对象是否存在该属性 65 if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) { 66 // 利用kvc赋值 67 [instance setValue:obj forKey:key]; 68 } 69 }]; 70 71 72 // 获取导航控制器 73 UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController; 74 UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex]; 75 76 // 跳转到对应的控制器 77 [pushClassStance pushViewController:instance animated:YES]; 78 } 79 80 /** 81 * 检测对象是否存在该属性 82 */ 83 - (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName 84 { 85 unsigned int outCount, i; 86 87 // 获取对象里的属性列表 88 objc_property_t * properties = class_copyPropertyList([instance 89 class], &outCount); 90 91 for (i = 0; i < outCount; i++) { 92 objc_property_t property =properties[i]; 93 // 属性名转成字符串 94 NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding]; 95 // 判断该属性是否存在 96 if ([propertyName isEqualToString:verifyPropertyName]) { 97 free(properties); 98 return YES; 99 } 100 } 101 free(properties); 102 103 return NO; 104 } 105 106 @end
1 //归档 2 - (void)encodeWithCoder:(NSCoder *)aCoder 3 { 4 unsigned int count; 5 //得到指向该类全部属性的指针 6 objc_property_t *properties = class_copyPropertyList([self class], &count); 7 8 for (int i = 0; i < count; i++) { 9 //得到该类一个属性的指针 10 objc_property_t property = properties[i]; 11 12 //得到属性的名称 13 const char *nameC = property_getName(property); 14 //C的字符串转成OC字符串 15 NSString *nameOC = [NSString stringWithUTF8String:nameC]; 16 17 //经过关键字取值 18 NSString *propertyValue = [self valueForKey:nameOC]; 19 //编码属性 20 [aCoder encodeObject:propertyValue forKey:nameOC]; 21 } 22 free(properties); 23 24 25 } 26 27 //解档 28 - (instancetype)initWithCoder:(NSCoder *)aDecoder 29 { 30 unsigned int count; 31 //得到指向该类全部属性的指针 32 objc_property_t *properties = class_copyPropertyList([self class], &count); 33 34 for (int i = 0; i < count; i++) { 35 //得到该类一个属性的指针 36 objc_property_t property = properties[i]; 37 38 //得到属性的名称 39 const char *nameC = property_getName(property); 40 //C的字符串转成OC字符串 41 NSString *nameOC = [NSString stringWithUTF8String:nameC]; 42 //解码属性值 43 NSString *propertyValue = [aDecoder decodeObjectForKey:nameOC]; 44 [self setValue:propertyValue forKey:nameOC]; 45 } 46 free(properties); 47 48 return self; 49 }
思路:利用运行时,遍历模型中全部属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。
步骤:提供一个NSObject分类,专门字典转模型,之后全部模型均可以经过这个分类转。
1 @implementation ViewController 2 3 - (void)viewDidLoad { 4 [super viewDidLoad]; 5 // Do any additional setup after loading the view, typically from a nib. 6 7 // 解析Plist文件 8 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"status.plist" ofType:nil]; 9 10 NSDictionary *statusDict = [NSDictionary dictionaryWithContentsOfFile:filePath]; 11 12 // 获取字典数组 13 NSArray *dictArr = statusDict[@"statuses"]; 14 15 // 自动生成模型的属性字符串 16 // [NSObject resolveDict:dictArr[0][@"user"]]; 17 18 19 _statuses = [NSMutableArray array]; 20 21 // 遍历字典数组 22 for (NSDictionary *dict in dictArr) { 23 24 Status *status = [Status modelWithDict:dict]; 25 26 [_statuses addObject:status]; 27 28 } 29 30 // 测试数据 31 NSLog(@"%@ %@",_statuses,[_statuses[0] user]); 32 33 34 } 35 36 @end 37 38 @implementation NSObject (Model) 39 40 + (instancetype)modelWithDict:(NSDictionary *)dict 41 { 42 // 思路:遍历模型中全部属性-》使用运行时 43 44 // 0.建立对应的对象 45 id objc = [[self alloc] init]; 46 47 // 1.利用runtime给对象中的成员属性赋值 48 49 // class_copyIvarList:获取类中的全部成员属性 50 // Ivar:成员属性的意思 51 // 第一个参数:表示获取哪一个类中的成员属性 52 // 第二个参数:表示这个类有多少成员属性,传入一个Int变量地址,会自动给这个变量赋值 53 // 返回值Ivar *:指的是一个ivar数组,会把全部成员属性放在一个数组中,经过返回的数组就能所有获取到。 54 /* 相似下面这种写法 55 56 Ivar ivar; 57 Ivar ivar1; 58 Ivar ivar2; 59 // 定义一个ivar的数组a 60 Ivar a[] = {ivar,ivar1,ivar2}; 61 62 // 用一个Ivar *指针指向数组第一个元素 63 Ivar *ivarList = a; 64 65 // 根据指针访问数组第一个元素 66 ivarList[0]; 67 68 */ 69 unsigned int count; 70 71 // 获取类中的全部成员属性 72 Ivar *ivarList = class_copyIvarList(self, &count); 73 74 for (int i = 0; i < count; i++) { 75 // 根据角标,从数组取出对应的成员属性 76 Ivar ivar = ivarList[i]; 77 78 // 获取成员属性名 79 NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)]; 80 81 // 处理成员属性名->字典中的key 82 // 从第一个角标开始截取 83 NSString *key = [name substringFromIndex:1]; 84 85 // 根据成员属性名去字典中查找对应的value 86 id value = dict[key]; 87 88 // 二级转换:若是字典中还有字典,也须要把对应的字典转换成模型 89 // 判断下value是不是字典 90 if ([value isKindOfClass:[NSDictionary class]]) { 91 // 字典转模型 92 // 获取模型的类对象,调用modelWithDict 93 // 模型的类名已知,就是成员属性的类型 94 95 // 获取成员属性类型 96 NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)]; 97 // 生成的是这种@"@\"User\"" 类型 -》 @"User" 在OC字符串中 \" -> ",\是转义的意思,不占用字符 98 // 裁剪类型字符串 99 NSRange range = [type rangeOfString:@"\""]; 100 101 type = [type substringFromIndex:range.location + range.length]; 102 103 range = [type rangeOfString:@"\""]; 104 105 // 裁剪到哪一个角标,不包括当前角标 106 type = [type substringToIndex:range.location]; 107 108 109 // 根据字符串类名生成类对象 110 Class modelClass = NSClassFromString(type); 111 112 113 if (modelClass) { // 有对应的模型才须要转 114 115 // 把字典转模型 116 value = [modelClass modelWithDict:value]; 117 } 118 119 120 } 121 122 // 三级转换:NSArray中也是字典,把数组中的字典转换成模型. 123 // 判断值是不是数组 124 if ([value isKindOfClass:[NSArray class]]) { 125 // 判断对应类有没有实现字典数组转模型数组的协议 126 if ([self respondsToSelector:@selector(arrayContainModelClass)]) { 127 128 // 转换成id类型,就能调用任何对象的方法 129 id idSelf = self; 130 131 // 获取数组中字典对应的模型 132 NSString *type = [idSelf arrayContainModelClass][key]; 133 134 // 生成模型 135 Class classModel = NSClassFromString(type); 136 NSMutableArray *arrM = [NSMutableArray array]; 137 // 遍历字典数组,生成模型数组 138 for (NSDictionary *dict in value) { 139 // 字典转模型 140 id model = [classModel modelWithDict:dict]; 141 [arrM addObject:model]; 142 } 143 144 // 把模型数组赋值给value 145 value = arrM; 146 147 } 148 } 149 150 151 if (value) { // 有值,才须要给模型的属性赋值 152 // 利用KVC给模型中的属性赋值 153 [objc setValue:value forKey:key]; 154 } 155 156 } 157 158 return objc; 159 } 160 161 @end
其余:http://www.jianshu.com/p/58c985408b75