如有不同看法,欢迎讨论。
一. RunTime是什么
runtime(简称运行时),是一套 纯C(C和汇编写的) 的API。而 OC 就是 运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。
这里要提的一点是,在C中的是无法调用未实现的函数的,否则会报错;但是在OC中,只要声明了方法就可以调用,只有在程序运行的时候才会报错,这一点也反映了OC的动态特性。
二. 动态特性
关于OC的动态特性主要体现在以下三个方面:1.动态类型:即运行时再决定对象类型。比如我们常见的id类型,任何对象都可以被id类型的指针所指,在运行时再确定具体类型;2.动态绑定:即在运行时决定该对象所拥有的对象及方法,一个例子就是在编写代码的时候,一个只声明未实现的方法依然可以被调用,但在运行时才会报错;3:动态载入:在运行时决定需要加载哪一部分资源,同样一个常见的例子就是通过不同机型决定加载@2x的图片还是@3x的图片。
三. Class的结构
首先是通过终端输入
clang -rewrite-objc MyClass.m
则会出现一个.cpp的C++编译的文件里边的代码很多,全局搜索
typedef struct objc_class *Class;
你会看到
typedef struct objc_class *Class;
struct objc_object {
Class isa __attribute__((deprecated));
};
即Class其实是一个objc_class类型的结构体,而objc_object是一个包含着isa指针的结构体,在runtime.h中我们可以看到objc_class的定义:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
其中具体的变量看英文名大体就能明白具体是干什么的,比如objc_method_list就是这个类所包含的方法列表,objc_ivar_list是成员变量列表,等等等等。同样我们也能通过Runtime取到这个类的属性,方法等;或者在运行中给类绑定属性,方法。
四. 消息发送流程
在这里我们已经知道,OC是一门动态语言,那么就有一个问题出来了,消息是怎么发送的,换言之当我们调用这个类的方法时发生了什么?
定义一个MyClass类,然后声明一个方法
- (void)showName:(NSString *)name;
OC 在向一个对象发送消息时,runtime 库会根据对象的isa指针遍历这个对象的methodLists,如果没找到,会遍历父类的methodLists,最终找到 SEL ,根据 id 和 SEL 确认 IMP(指针函数),在发送消息。如果找不到,则程序崩溃,报常见的unrecognized selector sent to class 0x10bb9b060,即没有找到实现的方法。
任何方法本质上都是向对象发送消息,比如我们常见的
MyClass *class = [MyClass alloc];
可以用runtime这样写
MyClass *class = ((MyClass * (*) (id, SEL)) objc_msgSend) (objc_getClass("MyClass"), sel_registerName("alloc"));
解释一下(id, SEL)是传入参数类型,也就是说,如果需要调用函数是带参数的,需要增加参数个数,例如:
int str = ((int (*) (id, SEL, int)) objc_msgSend) (objc_getClass("MyClass"), sel_registerName("showInt:"),12);
MyClass * ()是返回的类型,如果方法无返回值则是void ()。
先写到这里,至于Runtime的具体应用和应用场景再开一篇再写。