runtime中的消息机制

objc_msgSend的执行流程可以分为三大阶段:消息发送、动态方法解析、消息转发

1.消息发送

大致流程:先在本类的方法缓存列表中查找、再在本类的方法列表中查找(如果是类方法在元类的方法列表中查找)、如果还没找到继续在父类中查找。详细流程看下图:


0.消息发送.png

注:方法只会缓存在receiverClass的cache中,而不会在父类中缓存。

2.动态方法解析

如果第一步没有成功找到方法就会执行动态方法解析,动态方法解析实际上就是动态添加方法,当然这里的方法也分为对象方法和类方法,
看代码:

  - (void)eat{
NSLog(@"吃...");
}
// 动态添加实例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{

if (sel == @selector(eat)) {
    Method method = class_getInstanceMethod(self, sel);
    class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method));
    return YES;
}
return [super resolveInstanceMethod:sel];
}



+ (void)sleep{
NSLog(@"睡...");
}
// 动态添加类方法
+ (BOOL)resolveClassMethod:(SEL)sel{

if (sel == @selector(sleep)) {
    Method method = class_getClassMethod(self, sel);
    class_addMethod(object_getClass(self), sel, method_getImplementation(method), method_getTypeEncoding(method));
    return YES;
}
return [super resolveClassMethod:sel];
}

实例对象和类对象都能调用performSelector方法
最终动态添加的方法会被添加到方法列表中。
个人认为动态方法解析在实际开发中应用并不多。

3.消息转发

看图:


0.消息转发.png

消息转发分为实例对象和类对象(完整消息转发和快速转发)。
看代码:

  /*对象方法的消息转发**/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (aSelector == @selector(eat)) {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
[anInvocation invokeWithTarget:[Son new]];
}

方法签名的作用:获取函数的参数和返回值。


  /*类方法的消息转发**/
+ (id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(sleep)) {
    return [Son class];
}
return [super forwardingTargetForSelector:aSelector];
}

如果这个类没有实现这个方法,就把这个消息转发给实现了这个方法(能响应这个消息)的类。
之前两篇文章里(
1.iOS开发中的消息转发应用于解决NSTimer
2.iOS开发中的内存管理之解决NSTimer造成的循环引用

)提过利用消息转发解决NSTimer循环引用的问题。

两种不同的消息转发方式有什么不同呢?

1.快速消息转发只需实现一个方法,而完整消息转发需要实现两个方法。
2.快速消息转发只能转发给一个接收者,而完整消息转发可以转发给多个接收者。

转发给多个接收者代码


// 完整消息转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    if (aSelector == @selector(test:)) {
//        return [[[Student alloc]init] methodSignatureForSelector:aSelector];
        return [NSMethodSignature signatureWithObjCTypes:"i@:i"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
//    anInvocation.target = [[Student alloc]init];
//    [anInvocation invoke];
    // 转发给Student和Teacher两个接收者
    [anInvocation invokeWithTarget:[[Student alloc] init]];
    [anInvocation invokeWithTarget:[Teacher new]];
}

iOS开发中方法查找流程图

iOS开发中避免unrecognized selector出现的崩溃

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容