Objective-C 消息机制

2018-05-25  本文已影响0人  sleepwalker_man

前言

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方法,然后执行完成方法的调用。

上一篇 下一篇

猜你喜欢

热点阅读