引言
在Objective - C的开发中,Runtime 系统是其核心的动态特性支撑。在之前的文章里,我们已经对 isa
指针、class
结构和方法缓存等基础概念有了深入了解。这篇文章将继续深入探究 Runtime 里的几个关键部分,像 objc_msgSend
的三个阶段、super
的本质以及 isKindOf
和 isMemberOf
的底层实现等。
一、objc_msgSend 的三个阶段
1.1 消息机制概述
在Objective - C里,方法调用本质上是消息发送的过程。当代码里出现 [object method]
这样的方法调用时,编译器会把它转化为 objc_msgSend(object, @selector(method))
函数调用。objc_msgSend
函数承担着在运行时找到并执行对应方法的重要任务,其执行过程分为三个阶段:消息发送、动态方法解析和消息转发。
1.2 消息发送阶段
消息发送阶段是 objc_msgSend
的首要步骤,它的主要任务是查找方法的实现。具体流程如下:
- **检查接收者是否为
nil
**:要是接收者为nil
,消息发送会直接返回,不会有任何操作。这也是Objective - C中给nil
对象发送消息不会引发崩溃的原因。 - 查找方法缓存:先在接收者对象所属类的方法缓存里查找目标方法。由于方法缓存运用了哈希表结构,查找速度非常快。若在缓存中找到该方法,就直接调用其实现。
- 查找方法列表:若缓存中未找到目标方法,就会在接收者对象所属类的方法列表中查找。如果找到了,就将该方法添加到缓存中,再调用其实现。
- 查找父类:要是在当前类的方法列表中没找到目标方法,会沿着继承链向上,在父类的方法缓存和方法列表中继续查找,直至找到或者到达根类。
1.3 动态方法解析阶段
若在消息发送阶段没有找到目标方法,就会进入动态方法解析阶段。在这个阶段,Runtime 会调用 resolveInstanceMethod:
(针对实例方法)或者 resolveClassMethod:
(针对类方法)方法,让开发者有机会动态添加方法的实现。示例代码如下:
1 | @implementation MyClass |
在上述代码中,当调用 dynamicMethod
方法且该方法未找到时,resolveInstanceMethod:
会被调用,在这个方法里动态添加了 dynamicMethod
的实现。
1.4 消息转发阶段
若动态方法解析阶段也未能解决问题,就会进入消息转发阶段。消息转发分为两个步骤:
- 备用接收者:Runtime 会调用
forwardingTargetForSelector:
方法,询问是否有其他对象可以处理这个消息。若返回一个非nil
的对象,就会把消息转发给这个对象处理。示例代码如下:1
2
3
4
5
6- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(anotherMethod)) {
return anotherObject;
}
return [super forwardingTargetForSelector:aSelector];
} - 完整消息转发:若备用接收者阶段没有处理消息,Runtime 会调用
methodSignatureForSelector:
方法获取方法签名,接着调用forwardInvocation:
方法,开发者可以在这个方法里自定义消息的处理逻辑。示例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(yetAnotherMethod)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
if ([anotherObject respondsToSelector:selector]) {
[anInvocation invokeWithTarget:anotherObject];
} else {
[super forwardInvocation:anInvocation];
}
}
二、super 的本质
2.1 super 的基本概念
在Objective - C里,super
关键字常被用于调用父类的方法。不过,super
并非一个对象,而是一个编译器指示符。当使用 super
调用方法时,实际上是告诉编译器从父类开始查找方法的实现。
2.2 super 的底层实现
super
的底层是通过 objc_msgSendSuper
函数实现的。objc_msgSendSuper
函数接收一个 objc_super
结构体作为参数,这个结构体定义如下:
1 | struct objc_super { |
receiver
:消息的接收者,也就是当前对象。super_class
:开始查找方法实现的父类。
当使用 super
调用方法时,编译器会生成如下代码:
1 | struct objc_super superReceiver; |
这表明 super
调用方法时,接收者依然是当前对象 self
,只是从父类开始查找方法的实现。
2.3 super 使用示例
下面通过一个具体的例子来理解 super
的使用:
1 | #import <Foundation/Foundation.h> |
在上述代码中,Dog
类继承自 Animal
类,并重写了 eat
方法。在 Dog
类的 eat
方法中,使用 [super eat]
调用了父类 Animal
的 eat
方法,然后再执行自身的逻辑。运行这段代码,输出结果如下:
1 | Animal is eating. |
这说明 super
成功调用了父类的方法,并且当前对象依然是 Dog
实例。
三、isKindOf 和 isMemberOf 的底层实现
3.1 isKindOfClass 方法
isKindOfClass
方法用于判断一个对象是否是某个类或者其子类的实例。其底层实现逻辑如下:
1 | + (BOOL)isKindOfClass:(Class)cls { |
从上述代码可以看出,isKindOfClass
方法会沿着继承链向上查找,逐个比较对象所属类及其父类是否与传入的类相等,只要有一个相等就返回 YES
,否则返回 NO
。
3.2 isKindOfClass 示例
1 | #import <Foundation/Foundation.h> |
在这个例子中,Dog
类继承自 Animal
类,Animal
类继承自 NSObject
类。dog
实例调用 isKindOfClass
方法分别判断是否为 Dog
、Animal
和 NSObject
类的实例。运行结果如下:
1 | Is dog an instance of Dog: 1 |
这表明 dog
既是 Dog
类的实例,也是 Animal
类和 NSObject
类的实例,因为 isKindOfClass
会考虑继承关系。
3.3 isMemberOfClass 方法
isMemberOfClass
方法用于判断一个对象是否是某个类的直接实例,不包括子类。其底层实现逻辑如下:
1 | + (BOOL)isMemberOfClass:(Class)cls { |
从代码可知,isMemberOfClass
方法直接比较对象所属类是否与传入的类相等,只有相等时才返回 YES
,否则返回 NO
。
3.4 isMemberOfClass 示例
1 | #import <Foundation/Foundation.h> |
在这个例子中,dog
实例调用 isMemberOfClass
方法分别判断是否为 Dog
类和 Animal
类的直接实例。运行结果如下:
1 | Is dog a direct instance of Dog: 1 |
这表明 dog
是 Dog
类的直接实例,但不是 Animal
类的直接实例,因为 isMemberOfClass
不考虑继承关系。
四、总结
深入理解 objc_msgSend
的三个阶段、super
的本质以及 isKindOf
和 isMemberOf
的底层实现,能让我们更清晰地把握Objective - C的消息机制和继承体系。objc_msgSend
的三个阶段为方法调用提供了极大的灵活性,让我们可以在运行时动态添加和转发方法;super
的本质让我们明白它是如何调用父类方法的;isKindOf
和 isMemberOf
的底层实现则帮助我们准确判断对象与类之间的关系。这些知识对于优化代码性能、解决复杂问题以及实现高级功能都有着重要的意义。希望通过本文的介绍,你能对Objective - C Runtime有更深入的认识。