理解OC的Runtime

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;
}

参考文章

iOS:学习Runtime的理解和心得
Objective-C总Runtime的那点事儿(一)消息机制

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值