引言

在Objective - C的开发中,Runtime 系统是其核心的动态特性支撑。在之前的文章里,我们已经对 isa 指针、class 结构和方法缓存等基础概念有了深入了解。这篇文章将继续深入探究 Runtime 里的几个关键部分,像 objc_msgSend 的三个阶段、super 的本质以及 isKindOfisMemberOf 的底层实现等。

一、objc_msgSend 的三个阶段

1.1 消息机制概述

在Objective - C里,方法调用本质上是消息发送的过程。当代码里出现 [object method] 这样的方法调用时,编译器会把它转化为 objc_msgSend(object, @selector(method)) 函数调用。objc_msgSend 函数承担着在运行时找到并执行对应方法的重要任务,其执行过程分为三个阶段:消息发送、动态方法解析和消息转发。

1.2 消息发送阶段

消息发送阶段是 objc_msgSend 的首要步骤,它的主要任务是查找方法的实现。具体流程如下:

  1. **检查接收者是否为 nil**:要是接收者为 nil,消息发送会直接返回,不会有任何操作。这也是Objective - C中给 nil 对象发送消息不会引发崩溃的原因。
  2. 查找方法缓存:先在接收者对象所属类的方法缓存里查找目标方法。由于方法缓存运用了哈希表结构,查找速度非常快。若在缓存中找到该方法,就直接调用其实现。
  3. 查找方法列表:若缓存中未找到目标方法,就会在接收者对象所属类的方法列表中查找。如果找到了,就将该方法添加到缓存中,再调用其实现。
  4. 查找父类:要是在当前类的方法列表中没找到目标方法,会沿着继承链向上,在父类的方法缓存和方法列表中继续查找,直至找到或者到达根类。

1.3 动态方法解析阶段

若在消息发送阶段没有找到目标方法,就会进入动态方法解析阶段。在这个阶段,Runtime 会调用 resolveInstanceMethod:(针对实例方法)或者 resolveClassMethod:(针对类方法)方法,让开发者有机会动态添加方法的实现。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation MyClass

+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(dynamicMethod)) {
class_addMethod(self, sel, (IMP)dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}

void dynamicMethodIMP(id self, SEL _cmd) {
NSLog(@"Dynamic method called.");
}

@end

在上述代码中,当调用 dynamicMethod 方法且该方法未找到时,resolveInstanceMethod: 会被调用,在这个方法里动态添加了 dynamicMethod 的实现。

1.4 消息转发阶段

若动态方法解析阶段也未能解决问题,就会进入消息转发阶段。消息转发分为两个步骤:

  1. 备用接收者:Runtime 会调用 forwardingTargetForSelector: 方法,询问是否有其他对象可以处理这个消息。若返回一个非 nil 的对象,就会把消息转发给这个对象处理。示例代码如下:
    1
    2
    3
    4
    5
    6
    - (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(anotherMethod)) {
    return anotherObject;
    }
    return [super forwardingTargetForSelector:aSelector];
    }
  2. 完整消息转发:若备用接收者阶段没有处理消息,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
2
3
4
struct objc_super {
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
};
  • receiver:消息的接收者,也就是当前对象。
  • super_class:开始查找方法实现的父类。

当使用 super 调用方法时,编译器会生成如下代码:

1
2
3
4
struct objc_super superReceiver;
superReceiver.receiver = self;
superReceiver.super_class = [self superclass];
objc_msgSendSuper(&superReceiver, @selector(method));

这表明 super 调用方法时,接收者依然是当前对象 self,只是从父类开始查找方法的实现。

2.3 super 使用示例

下面通过一个具体的例子来理解 super 的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#import <Foundation/Foundation.h>

@interface Animal : NSObject
- (void)eat;
@end

@implementation Animal
- (void)eat {
NSLog(@"Animal is eating.");
}
@end

@interface Dog : Animal
- (void)eat;
@end

@implementation Dog
- (void)eat {
[super eat];
NSLog(@"Dog is eating.");
}
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] init];
[dog eat];
}
return 0;
}

在上述代码中,Dog 类继承自 Animal 类,并重写了 eat 方法。在 Dog 类的 eat 方法中,使用 [super eat] 调用了父类 Animaleat 方法,然后再执行自身的逻辑。运行这段代码,输出结果如下:

1
2
Animal is eating.
Dog is eating.

这说明 super 成功调用了父类的方法,并且当前对象依然是 Dog 实例。

三、isKindOf 和 isMemberOf 的底层实现

3.1 isKindOfClass 方法

isKindOfClass 方法用于判断一个对象是否是某个类或者其子类的实例。其底层实现逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
if (tcls == cls) return YES;
}
return NO;
}

从上述代码可以看出,isKindOfClass 方法会沿着继承链向上查找,逐个比较对象所属类及其父类是否与传入的类相等,只要有一个相等就返回 YES,否则返回 NO

3.2 isKindOfClass 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#import <Foundation/Foundation.h>

@interface Animal : NSObject
@end

@implementation Animal
@end

@interface Dog : Animal
@end

@implementation Dog
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] init];
BOOL result1 = [dog isKindOfClass:[Dog class]];
BOOL result2 = [dog isKindOfClass:[Animal class]];
BOOL result3 = [dog isKindOfClass:[NSObject class]];
NSLog(@"Is dog an instance of Dog: %d", result1);
NSLog(@"Is dog an instance of Animal: %d", result2);
NSLog(@"Is dog an instance of NSObject: %d", result3);
}
return 0;
}

在这个例子中,Dog 类继承自 Animal 类,Animal 类继承自 NSObject 类。dog 实例调用 isKindOfClass 方法分别判断是否为 DogAnimalNSObject 类的实例。运行结果如下:

1
2
3
Is dog an instance of Dog: 1
Is dog an instance of Animal: 1
Is dog an instance of NSObject: 1

这表明 dog 既是 Dog 类的实例,也是 Animal 类和 NSObject 类的实例,因为 isKindOfClass 会考虑继承关系。

3.3 isMemberOfClass 方法

isMemberOfClass 方法用于判断一个对象是否是某个类的直接实例,不包括子类。其底层实现逻辑如下:

1
2
3
4
5
6
7
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
}

从代码可知,isMemberOfClass 方法直接比较对象所属类是否与传入的类相等,只有相等时才返回 YES,否则返回 NO

3.4 isMemberOfClass 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#import <Foundation/Foundation.h>

@interface Animal : NSObject
@end

@implementation Animal
@end

@interface Dog : Animal
@end

@implementation Dog
@end

int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc] init];
BOOL result1 = [dog isMemberOfClass:[Dog class]];
BOOL result2 = [dog isMemberOfClass:[Animal class]];
NSLog(@"Is dog a direct instance of Dog: %d", result1);
NSLog(@"Is dog a direct instance of Animal: %d", result2);
}
return 0;
}

在这个例子中,dog 实例调用 isMemberOfClass 方法分别判断是否为 Dog 类和 Animal 类的直接实例。运行结果如下:

1
2
Is dog a direct instance of Dog: 1
Is dog a direct instance of Animal: 0

这表明 dogDog 类的直接实例,但不是 Animal 类的直接实例,因为 isMemberOfClass 不考虑继承关系。

四、总结

深入理解 objc_msgSend 的三个阶段、super 的本质以及 isKindOfisMemberOf 的底层实现,能让我们更清晰地把握Objective - C的消息机制和继承体系。objc_msgSend 的三个阶段为方法调用提供了极大的灵活性,让我们可以在运行时动态添加和转发方法;super 的本质让我们明白它是如何调用父类方法的;isKindOfisMemberOf 的底层实现则帮助我们准确判断对象与类之间的关系。这些知识对于优化代码性能、解决复杂问题以及实现高级功能都有着重要的意义。希望通过本文的介绍,你能对Objective - C Runtime有更深入的认识。