iOS runtime详解
runtime(运行时机制)###
简介####
1.Runtime是一套底层的纯C语言的API,属于C语言的一个库,里面包含了很多底层的C语言的API
2.平时编写的OC代码,在程序运行过程中,其实最终都转成了runtime的C语言的代码,runtime算是OC的幕后工作者
3.比如这段OC代码:
XPerson *obj = [[XPerson alloc] init];
obj.name = @"syj";
runtime C 代码:
XPerson *obj = objc_msgSend(objc_msgSend(objc_getClass("XPerson),@selector(alloc)),@selector(init));
objc_msgSend(obj,@selector(setName:),@"syj");
runtime的作用####
1.在程序运行中动态创建一个类(比如KVC、KVO的底层实现)
2.在程序运行中动态为某个类添加属性和方法,修改属性的值和方法
3.遍历一个类的所有属性和方法(字典转模型,归档时用循环实现)
runtime相关应用####
1.NSCoding(归档和解档)
2.字典转模型(json解析封装的那个函数)
3.KVO(利用runtime动态生成一个类)
4.可以用于开发框架(方便更改)
runtime相关的头文件####
#import <objc/runtime.h> 包含对类、成员变量、属性、方法的操作
#import <objc/message.h> 包含消息机制
runtime常用方法####
objc_msgsend:给对象发送消息
class_copyMethodList:遍历某个类所有的方法
class_copyIvarList:遍历某个类所有的成员变量
runtime官方文档(部分摘选)####
Method#####
Method 代表类中某个方法的类型
typedef struct objc_method *Method;
struct objc_method {
SEL method_name //方法名类型为SEL
char *method_types //方法类型是个char指针,存储方法的参数类型和返回值类型
IMP method_imp //method_imp指向了方法的实现,本质是一个函数指针
}
objc_method存储了方法名,方法类型,方法实现
Ivar#####
Ivar 是表示成员变量的类型
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name
char *ivar_type
int ivar_offset //基地址偏移字节
#ifdef __LP64__
int space
#endif
}
Category#####
typedef struct objc_category *Category;
struct objc_category {
char *category_name
char *class_name
struct objc_method_list *instance_methods
struct objc_method_list *class_methods
struct objc_protocol_list *protocols
}
Property#####
typedef struct objc_property *objc_property_t;
可以通过class_copyPropertyList和protocol_copyPropertyList方法获取类和协议中的属性
Class#####
Class是指向objc_class结构体的指针
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class //父类指针
const char *name //类名
long version
long info
long instance_size
struct objc_ivar_list *ivars //成员变量列表
struct objc_method_list **methodLists //方法列表
struct objc_cache *cache //缓存
struct objc_protocol_list *protocols //协议
#endif
}
/* Use `Class` instead of `struct objc_class *` */
字典转模型####
+ (NSMutableArray *)customModel:(NSString *)modelClass ToArray:(id)jsonArray
{
//用来存放一个类中有几个成员变量。
unsigned int outCount = 0;
//这句话执行之后outCount的值就会是当前类中属性的个数。整体返回的是一个指针数组,里面包含对应类的各个属性信息。
objc_property_t *properties = class_copyPropertyList(objc_getClass(modelClass), &outCount);
//创建一个数组用来存放对象;
NSMutableArray *objectArr = [[NSMutableArray alloc] initWithCapacity:1];
for (int i = 0; i < [jsonArray count]; i++)
{
//取出数组中的一个对象
id jsonDic = [jsonArray objectAtIndex:i];
//若数组中的对象不是字典对象就跳过它。继续下一个。
if(![jsonDic isKindOfClass:[NSDictionary class]])
{
continue;
}
//创建一个传过来字符串(类名)相应的对象
id model = [[objc_getClass(modelClass) alloc] init];
// [model setValuesForKeysWithDictionary:jsonDic];
//判断类中的每一个属性
for (int j = 0; j < outCount; j++)
{
//获取类中的第j个成员变量信息
objc_property_t property = properties[j];
//获取类中的第j个成员变量将其转化为字符串
NSString *propertyName =[NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
//获得json串中的键对应的值
id value = [jsonDic objectForKey:propertyName];
//判断上面取得的值是否存在,不存在就去转换下一个属性
if(!value || [value isKindOfClass:[NSNull class]])
{
continue;
}
[model setValue:value forKey:propertyName];
}
//把转化完成的对象加到一个数组中。
[objectArr addObject:model];
}
CFRelease(properties);
return objectArr;
}
归档解档####
把自己定义的类所创建的对象直接写入文件的步骤:
自定义类遵循NSCoding协议,实现NSCoding协议中的两个方法:
encodeWithCoder:往文件中写入实例变量
initWithCoder:从文件中读取实例变量为当前对象赋值
如何把对象写入文件:调用NSKeyedArchiver中的archiverRootObject:toFile:
如何把对象从文件中读取出来:调用NSKeyedUnarchiver中的unarchiveObjectWithFile:
代码如下:
//归档(将数据存入文件)
- (void)encodeWithCoder:(NSCoder *)aCoder
{
//归档存储自定义对象
unsigned int count = 0;
//获得指向该类所有成员变量的指针
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; ++i)
{
Ivar ivar = ivars[i];
//获得成员变量的名称
const char *keyName = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:keyName];
//编码每个属性,利用KVC取出每个属性对应的值
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
//解档并初始化(读取文件中的数据)
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super init])
{
unsigned int count = 0;
//获取指向该类所有成员变量的指针
Ivar *ivars = class_copyIvarList([self class], &count);
for (int i = 0; i < count; ++i)
{
Ivar ivar = ivars[i];
//获得成员变量的名称
const char *keyName = ivar_getName(ivar);
NSString *key = [NSString stringWithUTF8String:keyName];
//解码每个属性,利用KVC为每个属性赋值
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}