对于iOS的开发者,相信Objc的runtime机制肯定都听说过,了解runtime的机制,对自己的提高不言而喻,最近在一直学习runtime机制,对此写下点学习过程的收获,以便以后查阅,也方便理解,本人研究runtime时间不长,如果有理解不对的地方也请告知,共同进步。
什么叫runtime呢,大家也都知道Objc的底层是C语言完成的,在运行的时候,会将Objc的代码转换成C语言的代码,这段过程就是所说的运行时runtime。比如执行一个方法的执行: [target action];这段代码会在runtime的时候转换为objc_msgSend(target,@selector(action));
要想了解RunTime,首先了解的必然是类的结构,在Objc中所有的东西都可以看做是一个对象,实例对象是一个对象,类也是一个对象,打开objc/runtime.h,研究一下他的结构:
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;//描述类的一个方法
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;//实例变量
/// An opaque type that represents a category.
typedef struct objc_category *Category;//类目
/// An opaque type that represents an Objective-C declared property.
typedef struct objc_property *objc_property_t;//类中声明的属性
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;//指向类结构的指针
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;//父类,如果此类是根类,则为NULL
const char *name OBJC2_UNAVAILABLE;//类名
long version OBJC2_UNAVAILABLE;//版本号
long info OBJC2_UNAVAILABLE;//标志位
long instance_size OBJC2_UNAVAILABLE;//实例对象的大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;//实例变量的列表,没有则为NULL
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;//存储对象方法列表,没有则为NULL
struct objc_cache *cache OBJC2_UNAVAILABLE;//缓存最近使用过的方法,提高运行效率
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;//存储遵守正式协议的列表,没有则为NULL
#endif
} OBJC2_UNAVAILABLE;
看到这段代码,想起之前看到过罗朝辉出过的一本书,上面在这里面讲的一句话是"内存布局以一个objc_class指针为开始的所有东东都可以当做一个object来对待"。
isa指针是个什么东东呢,它是一个Class类型的指针,如果这个类是一个实例对象(instance object),那么它的isa指针就指向它所属于的类对象的类(class 实例对象的isa指针指向的类对象为class,class中存储着普通成员变量以及普通的方法,即-方法),如果这个类是一个类对象(class object),那么它的isa指针就指向了metaClass(类对象isa指向的类结构为metaclass,metaclass中存储着类的Static类成员变量以及Static方法,即+方法)
如果有点乱的话,官方有一张图很清晰的描述了他们之间的关系,如下
只说无代码是枯燥的,为了测试一下简单的RunTime机制,创建一个RunTimeTextClass类
//
// RunTimeTextClass.h
// RunTime
//
// Created by YueWen on 16/2/21.
// Copyright © 2016年 YueWen. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol Protocol1;
@protocol Protocol2;
@interface RunTimeTextClass : NSObject<Protocol1>
{
NSString * ival1;
NSString * ival2;
}
@property (copy, nonatomic)NSString * title;
@property (assign, nonatomic)NSInteger index;
- (void)objctFunction;
+ (void)classFunction;
@end
@protocol Protocol1 <NSObject>
@optional
- (void)text1;
@end
@protocol Protocol2 <NSObject>
@optional
- (void)text2;
@end
//
// RunTimeTextClass.m
// RunTime
//
// Created by YueWen on 16/2/21.
// Copyright © 2016年 YueWen. All rights reserved.
//
#import "RunTimeTextClass.h"
@interface RunTimeTextClass()<Protocol2>
{
NSString * ival1M;
}
@property (nonatomic, copy)NSString * titleM;//.m中的属性
@end
@implementation RunTimeTextClass
-(void)objctFunction
{
}
@end
获取类的属性列表
//获取类的属性列表
unsigned int propertyCount;
objc_property_t * propertyList = class_copyPropertyList([RunTimeTextClass class], &propertyCount);//获取属性的数组列表
for (int i = 0; i < propertyCount; i ++)//开始遍历
{
//获取属性名字
const char * name = property_getName(propertyList[i]);
NSLog(@"property = %@",[NSString stringWithUTF8String:name]);
}
打印结果如下:
获取类的实例变量列表
//获取类的实例变量列表
unsigned int ivarCount;
Ivar * ivars = class_copyIvarList([RunTimeTextClass class], &ivarCount);//获取实例变量的列表
for (int i = 0; i < ivarCount; i++)//开始遍历
{
//获取实例变量的名字
const char * name = ivar_getName(ivars[i]);
NSLog(@"ivar = %@",[NSString stringWithUTF8String:name]);
}
打印结果如下: 从结果来看,每创建一个属性,运行时会响应的创建一个_(propertyName)的实例变量
获取类中已经实现的方法列表
//获取已经实现的方法列表
unsigned int methodCount;
Method * methods = class_copyMethodList([RunTimeTextClass class], &methodCount);
for (int i = 0; i < methodCount; i++)
{
//获取方法名字
Method method = methods[i];
NSLog(@"method = %@",NSStringFromSelector(method_getName(method)));
}
运行结果如下:看出没有实现的方法并没有进行打印,并且用@property声明的变量在RunTime会自行的创建它的Get和Set方法
获取遵守的协议列表
//获取协议的列表
unsigned int protocolCount;
__unsafe_unretained Protocol ** protocols = class_copyProtocolList([RunTimeTextClass class], &protocolCount);
for (int i = 0; i < protocolCount; i++)
{
//获取协议
Protocol * protocol = protocols[i];
//获得协议名称
const char * name = protocol_getName(protocol);
NSLog(@"Protocol = %@",[NSString stringWithUTF8String:name]);
}
运行结果如下:(Protocol2是在延展中遵守,Protocol1是在声明文件中遵守)
RunTime到底有什么用呢,比如字典转模型的时候,我想属性的名字和字典的索引名字相同,但是实际上属性的名称与字典的key值并不匹配,那么就可以在RunTime中对类进行添加属性。RunTime研究起来很有意思,想以后会陆续的写一些关于RunTime的东西。