1.方法的本质
通过之前分析对象的本质,同理,我们也可以通过clang来分析方法的本质。
首先创建Person类,在Person类中我们先实现一个方法:
.h
@interface Person : NSObject
- (void)sayHi;
@end
.m
#import "Person.h"
@implementation Person
- (void)sayHi {
NSLog(@"hi");
}
@end
然后我们在main函数中调用一下person的方法:
Person *person = [[Person alloc] init];
[person sayHi];
随后转成C++代码看下内部实现:
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
打开main.cpp文件,可以全局搜索我们的“sayHi”方法,查看
转换前:
Person *person = [[Person alloc] init];
[person sayHi];
转换后:
Person *person = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)person, sel_registerName("sayHi"));
可以看出,实例对象调用方法,本质是通过objc_msgSend(消息发送)实现的。
接下来我们来验证一下它们是不是真的一样:
Person *person = [[Person alloc] init];
//方法调用
[person sayHi];
//消息发送
objc_msgSend(person, @selector(sayHi));
打印结果如下,两种打印结果一致,验证cheng
注意: 1.调用objc_msgSend,需要导入头文件 #import <objc/message.h>
2.使用objc_msgSend时,编译器会报错。需要将 target —> Build Setting —> 搜索msg —> 将Enable Strict Checking of objc_msgSend(Calls objc_msgSend的严格检查机制)关掉
2.方法的调用
下面的代码,Student 继承自 Person,Person 实现了 sayHi 方法,Student 确没有实现。
@interface Person : NSObject
- (void)sayHi;
@end
@implementation Person
- (void)sayHi {
NSLog(@"hi");
}
@end
@interface Student : Person
- (void)sayHi;
- (void)sayHello;
@end
@implementation Student
- (void)sayHello {
NSLog(@"hello");
}
@end
消息的接收者是 Student的实例,分别使用 OC 对象调用和 C语言的 API 调用:
- OC对象的调用
Person *person = [[Person alloc] init];
Student *student = [[Student alloc] init];
[student sayHi];
- 直接调用底层C的API
struct objc_super stusuper;
stusuper.receiver = student;
stusuper.super_class = [Person class];
objc_msgSendSuper(&stusuper, sel_registerName("sayHi"));
objc_msgSendSuper 的定义:
OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * super, SEL op, ...)
#endif
/// Specifies the superclass of an instance. 指定实例的超类
struct objc_super {
/// Specifies an instance of a class. 指定一个类的实例
__unsafe_unretained _Nonnull id receiver;
/// Specifies the particular superclass of the instance to message. 指定要传递消息的实例的特定超类
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header 兼容旧的objc-runtime.h头文件 */
__unsafe_unretained _Nonnull Class class;
#else
__unsafe_unretained _Nonnull Class super_class;
#endif
/* super_class is the first class to search */
};
#endif
在OBJC2中,需要传入 receiver 和 super_class。
运行,打印结果一致:
2022-08-10 17:14:14.914094+0800 objc[18959:210368] hi
2022-08-10 17:14:14.914596+0800 objc[18959:210368] hi
可见,Student 并没有实现 sayHi,却没有崩溃,而且两种方法都打印出来了,猜测是因为其父类实现了sayHi。
可猜想,类的实例方法的调用是会像父类查找。而方法的本质是 objc_msgSend 发送消息,那么它是怎么通过 sel 找到 imp 函数地址的指针 ,从而找到函数的具体实现呢?
带着这个疑惑我们来查看 objc_msgSend 的流程。