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;
}
posted @ 2016-10-24 20:22  狼牙战士  阅读(240)  评论(0编辑  收藏  举报