OC的Runtime是指运行时,顾名思义在编译过程中并不能真正决定要调用哪个方法,要等运行的时候,才根据方法的名称找到相应的方法来执行。
消息发送机制
当调用一个对象的方法时,实际上是在给对象发送消息,OC代码会被转化成Runtime的C代码执行,[obj target]会被转化成:objc_msgSend(obj,@selector(target));
然后这个函数通过对象的isa找到相应的Class。先在Class 的cache中对应的方法,如果未找到,再去methodList中查找,若methodList中未找到,则去superClass中的methodList中查找。
最后,若能找到,则将方法加入cache中,以便下次查找(这样能提高性能),然后执行这个方法。
若找不到,转向‘拦截调用方法’,如果‘拦截调用方法’没有重写,抛出异常。
拦截调用方法
在找不到方法、程序崩溃之前,你还有机会重写NSObject的四个方法来处理
// 处理类方法
+ (BOOL)resolveClassMethod:(SEL)sel;
// 处理实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel;
// 重定向到另一个实现了该方法的对象中,只需要返回这个对象即可
- (id)forwardingTargetForSelector:(SEL)aSelector;
// 把那个不存在的方法打包成NSInvocation传给你。做完你自己的处理后,调用invokeWithTarget:方法让某个target触发这个方法。
- (void)forwardInvocation:(NSInvocation *)anInvocation;
+resolveInstanceMethod:或者 +resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数并返回 YES,那运行时系统就会重新启动一次消息发送的过程,如果 resolve 方法返回 NO ,运行时就会移到下一步:消息转发
如果目标对象实现了-forwardingTargetForSelector:,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。 只要这个方法返回的不是nil和self,整个消息发送的过程就会被重启,当然发送的对象会变成你返回的那个对象。否则,就会继续Normal Fowarding。 这里叫Fast,只是为了区别下一步的转发机制。因为这一步不会创建任何新的对象,但下一步-forwardInvocation:会创建一个NSInvocation对象,所以相对更快点
Normal forwarding,这一步是Runtime最后一次给你挽救的机会。首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型,如果返回nil,Runtime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。
关联对象
如果只想给一个类添加一个属性,那么重新写一个类并继承它显得很麻烦,这时Runtime的‘关联对象函数’就发挥作用了。
在使用‘关联对象函数’之前,要引入Runtime的库
#import <objc/runtime.h>
将‘关联对象函数’封装为方法,放在你的categroy中
//添加关联对象
- (void)addAssociatedObject:(id)object {
objc_setAssociatedObject(self, @selector(getAssociatedObject), object, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//获取关联对象
- (id)getAssociatedObject {
return objc_getAssociatedObject(self, _cmd);
}
获取列表
有时候需要知道对象中的属性名和函数名,可以用Runtime的‘获取列表函数’
同样将这几个函数封装成方法,放在你的category中
- (NSArray*)dptPropertyList {
NSMutableArray *resultList = [[NSMutableArray alloc] initWithCapacity:3];
unsigned int count;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (unsigned int i=0; i<count; i++) {
const char *propertyName = property_getName(propertyList[i]);
[resultList addObject:[NSString stringWithUTF8String:propertyName]];
}
return resultList;
}
- (NSArray*)dptMethodList {
NSMutableArray *resultList = [[NSMutableArray alloc] initWithCapacity:3];
unsigned int count;
Method *methodList = class_copyMethodList([self class], &count);
for (unsigned int i=0; i<count; i++) {
Method method = methodList[i];
SEL methodSEL = method_getName(method);
const char *methodName = sel_getName(methodSEL);
[resultList addObject:[NSString stringWithUTF8String:methodName]];
}
return resultList;
}