Objective-C 消息机制
前言
Objective-C(以下简称OC),是一门动态语言。OC的动态性体现在动态类型(OC代码在编译时不会比对类型是否匹配,只有在运行时根据不同的情况来识别类型是是否匹配)、动态绑定(代码在运行时判断需要调用什么方法,而不是在编译时)、动态加载(按需加载)。在OC中“函数调用的过程”就是“消息机制” 简单说 OC中任何方法的调用,本质都是发送消息。
了解消息机制之前先要重新认识下Object和Class的结构。
先贴上OC中Class和Object的定义源码:
Object
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
Class
typedef struct objc_class *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;
/* Use `Class` instead of `struct objc_class *` */
从源码上可以看出 Object和Class都是结构体,下面通过表格的样式具体看下结构体中不同类型的数据各自代表的含义(这块详细内容请移步博客地址)
Object
数据 | 含义 |
---|---|
isa | 指向objc_class类型的指针 |
Class
数据 | 含义 |
---|---|
super_class | 指向该类的父类的指针 |
isa | 指向objc_class类型的指针 |
name | 类名 |
version | 类的版本信息 |
info | 运行期的位标识 |
instance_size | 该类实例变量大小(包括从父类继承下来的实例变量) |
ivars | 指向objc_ivar_list的指针,存储实例变量内存地址(个人认为是该类的属性组、如有偏差请指正) |
methodLists | 与 info 的一些标志位有关,实例方法或者类方法 |
cache | 指向objc_cache的指针,缓存最近使用的方法 |
protocols | 指向 objc_protocol_list 的指针,存储该类声明要遵守的正式协议 |
附上述博客中提到的info的解释(如有异议请指出)
info:供运行期使用的一些位标识。有如下一些位掩码:
CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含实例方法和变量;
CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;
CLS_INITIALIZED (0x4L) 表示该类已经被运行期初始化了,这个标识位只被 objc_addClass 所设置;
CLS_POSING (0x8L) 表示该类被 pose 成其他的类;(poseclass 在 ObjC 2.0 中被废弃了);
CLS_MAPPED (0x10L) 为 ObjC 运行期所使用;
CLS_FLUSH_CACHE (0x20L) 为 ObjC 运行期所使用;
CLS_GROW_CACHE (0x40L) 为 ObjC 运行期所使用;
CLS_NEED_BIND (0x80L) 为 ObjC 运行期所使用 ;
CLS_METHOD_ARRAY (0x100L) 该标志位指示 methodlists 指向一个 objc_method_list 还是 一个包含 objc_method_list 指针的数组;
metaClass
显而易见的发现不管是object还是Class都存在一个isa指针,object的isa指针指向了object对应的类,但是Class的isa指针指向谁呢?继续学习,发现这里有一个metaClass的概念。有关metaClass的解释此博客做了很详细的探究,这是篇英文,附上中文翻译版。通过Class的isa指针可以得出结论:Class通样也是一个对象!因为每个Class对象的方法不一致,由 metaClass决定了Class的特殊性(每每个类都有自己不同的方法列表),当你向一个类发送消息,就在Class的metaClass中查找消息。
介绍完Object和Class的结构,终于开始消息机制的学习了,上文指出OC中“函数调用的过程”被称为消息发送的过程,那me消息发送的过程到底是什么样的呢?举个简单的例子,现在有个Person类并且声明了一个实例方法eat。引入Person初始化一个Person对象,并调用eat方法
#import <Foundation/Foundation.h>
@interface Person : NSObject
- (void)eat;
@end
Person *p = [[Person alloc] init];
[p eat];
方法很简单,实际上这里消息和方法在运行阶段绑定在一起,涉及到objc_msgSend这个方法,看一下objc_msgSend在官方文档中的解释:
When it encounters a method call, the compiler generates a call to one of the functions objc_msgSend, objc_msgSend_stret, objc_msgSendSuper, or objc_msgSendSuper_stret. Messages sent to an object’s superclass (using the super keyword) are sent using objc_msgSendSuper; other messages are sent using objc_msgSend. Methods that have data structures as return values are sent using objc_msgSendSuper_stret and objc_msgSend_stret.
中文翻译为:
当遇到方法调用时,编译器会生成一个(objc_msgSend, objc_msgSend_stret, objc_msgSendSuper,或objc_msgSendSuper_stret )消息发送函数中的一个。发送到对象的父类(使用super关键字)的消息使用objc_msgSendSuper发送;其他消息使用objc_msgSend发送。使用objc_msgSendSuper_stret和objc_msgSend_stret发送具有数据结构作为返回值的方法。
objc_msgSend(void /* id self, SEL op, ... */ )
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);
那么根据官方的API,在#import <objc/message.h>
后,我们可以这样写eat调用的方法:
objc_msgSend(p,@selector(eat));
在网上找到一张关于消息发送过程的图解 消息发送如果objc_msgSend方法报错,请在Xcode中做如下操作
屏幕快照 2018-05-25 上午1.52.53.png
不考虑cache简单说上述Person实例对象p调用eat的过程是
首先执行performSelector:@selector(eat),obejct(p)的isa指针指向Class(Person) 在Class(Person)的methodLists中根据方法编号找到eat方法,然后执行完成方法的调用。