Objective-C Runtime 之一 基础介绍
本系列笔记参考 Runtime 源码为 objc4-779.1。
什么是 Runtime
Runtime 是一套使用 C、C++、汇编实现的 API,调用 ObjC 方法时,实际上是执行的 Runtime 接口,最终执行的其实是 C/C++ 代码。
Runtime 可以理解为构成 ObjC 语言的一部分。ObjC 为 C 语言加上的面向对象的特性,通过 Clang 编译器和 Runtime 实现。
编译 ObjC 文件
使用 clang -rewrite-objc Hello.m 命令。
使用 Runtime
ObjC 方法调用
ObjC 方法调用本质上就是在调用 Runtime 的 API,比如使用 ObjC 编写如下代码:
Person *olivia = [[Person alloc] init];
[olivia sayHi];
使用命令 clang -rewrite-objc main.m 编译成 C++ 代码后如下:
Person *olivia = ((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)olivia, sel_registerName("sayHi"));
可以看到,ObjC 中调用方法本质上是在调用 objc_msgSend 函数:
// message.h
OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
这个函数第一个参数就是方法的调用者(receiver),第二个参数是 SEL 类型,上面代码中使用 sel_registerName("方法名") 获得,如果方法含有参数,那么就是 objc_msgSend 中第三个以及之后的参数。其中 SEL 就是 方法选择器(selector),我们常常使用 @selector() 获得,它实际上是一个结构体指针。
// objc.h
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
而 sel_registerName 就是通过方法名这个字符串去获得 SEL:
OBJC_EXPORT SEL _Nonnull
sel_registerName(const char * _Nonnull str)
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
需要注意的是,这里说的方法名包含参数个数信息,不包含参数类型和返回值类型信息,也就是说,我们可以从一个 SEL 中得到这个方法的名字以及有几个参数(如 sayHi: 表示这个方法具有一个参数),但是无法知道这个方法的返回值类型和参数类型。所以 ObjC 中如果函数名相同且参数个数相同,即使返回值类型和参数类型都不同,编译器也是会报错的。
NSObject 方法
NSObject 中的方法大多都是调用的 Runtime 的 API:
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
- (BOOL)isProxy;
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
直接调用 Runtime 库函数
#import <objc/runtime.h>
#import <objc/message.h>
可以通过引入 Runtime 头文件,直接调用 Runtime 中的 API,基于 Runtime 的很多实现也是通过调用 Runtime API 完成的。