ios消息转发机制

本文深入探讨Objective-C的运行时机制,包括运行时与编译时的区别,Runtime的三种调用方式,方法的本质及objc_msgSend的作用,以及方法调用的快慢流程。通过详细的结构体说明和代码示例,帮助读者理解Objective-C动态特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运行时

定义:

运行时:是装载在内存,提供运行时功能(运行时的功能依赖runtime)
runtime:是一套由C、C++、汇编一起写成的api,给OC提供运行时

编译时

编译时:是源代码翻译成机器能识别的代码的过程,主要是对语言进行最基本的检查报错,即词法分析、语法分析等,是一个静态的阶段

Runtime的三种调用方式

  • 通过OC代码:[p sayHello]
  • 通过NSObject方法:isKindOfClass、isMeberOfClass
  • 通过Runtime API:class_getInstanceSize,objc_msgSend

什么是方法

我们先来看一下方法的结构体

struct objc_method
{
  // 方法名:方法名为此方法的签名,有着相同函数名和参数名的方法有着相同的方法名。
  SEL method_name;
  // 方法类型:方法类型描述了参数的类型。
  char * method_types;
  // IMP: IMP即函数指针,为方法具体实现代码块的地址,可像普通C函数调用一样使用IMP。
  IMP method_imp;
};
typedef objc_method Method;

方法的本质

编译器通过clang.m编译成.cpp文件

//: .main object代码
GomuPerson *person = [GomuPerson alloc];
[person sayNB];
[person sayHello];

//: .cpp 编译后的c++代码
GomuPerson *person = ((GomuPerson *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("GomuPerson"), sel_registerName("alloc"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayNB"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHello"));

结论:方法的本质就是objc_msgSend消息发送

objc_msgSend其实是由汇编写的

方法是怎么调用的

知识补充:

1.实例对象、类对象、元类对象详细解释请参看-博主ios知识点分享二第13条

2.在Objective-C中,比如有一个person类,该类有一个实例方法为:-(void)run;

调用形式如同 [person run],被称为消息发送,即“对person对象发送run消息”,

[person run]会被翻译成objc_msgSend(person, @selector(run)),Objective-C 方法查找是通过@selector(run)返回值字符串匹配查找的,远远没有静态函数的调用高效,所以在源码层添加了一层缓存(cache类型的源码在OC底层全部用汇编实现,优点是快),主要缓解多次查找低效问题。

方法调用分两步:

第一步:CacheLookup快速查询(即从缓存中获取方法)。

第二步:我们发现如果CacheLookup流程找不到的话,

就会进入CheckMiss -> __objc_msgSend_uncached -> MethodTableLookup -> lookUpImpOrForward流程,lookUpImpOrForward也就是我们今天要研究的慢速查找流程。

方法查找快流程

方法查找慢流程

 1.Method resolution 方法解析处理阶段

如果调用了方法 首先会调用

+(BOOL)resolveInstanceMethod:(SEL)sel【对象方法】,

+(BOOL)resolveClassMethod:(SEL)sel【类方法】进行判断,

如果YES能接收消息,NO就会进行第二步。

+(BOOL)resolveInstanceMethod:(SEL)sel;
+(BOOL)resolveClassMethod:(SEL)sel;

2.Fast forwarding 快速转发阶段

如果第一步返回NO,则进入消息转发第二步。主要下面这个API

-(id)forwardingTargetForSelector:(SEL)aSelector;

此方法可以将本类不能处理的方法转发给其他对象进行处理。

如下:

-(id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"%s-%@",__func__,NSStringFromSelector(aSelector));
 
    if (aSelector == @selector(method1)) {
        return [OtherObject alloc];
    }
    return [super forwardingTargetForSelector:aSelector];
}

3.Normal forwarding 常规转发阶段

如果第二返回self或nil,则进入该步骤。主要下面这两个API。

-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"%s-%@",__func__,NSStringFromSelector(aSelector));
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
 
-(void)forwardInvocation:(NSInvocation *)anInvocation {
    
    OtherObject *t = [OtherObject alloc];
    if ([self respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self];
    }else if ([t respondsToSelector:anInvocation.selector] ) {
        [anInvocation invokeWithTarget:t];
    }else {
        NSLog(@"%s-%@",__func__,NSStringFromSelector(anInvocation.selector));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值