iOS程序员iOS开发记录

Cocoa 消息机制 (Objective-C 反射相关)

2015-08-23  本文已影响405人  TerryZhang

Cocoa 消息机制 (Objective-C 反射相关)

相关概念

动态语言

程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。

Object-C 是一门动态语言

Java反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

JAVA反射(放射)机制:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制:Reflection,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。

Cocoa 消息机制

基本反射

Class 相关的操作

Selector 相关操作

相当于 Java 中的 Method,不过在 Mac 中,SEL是一个单例,即[Foo count]和[Bar count] 里面的count 指向的是同一个指针

Protocal 相关操作

继承关系

核心结构

Object & Class

每一个类实例对象的第一个实例变量是一个指向该对象的类结构的指针,叫做isa。

[self class];(NSObject 协议) 就是返回这个 isa;

Class 是指向类结构体的指针,该类结构体含有一个指向其父类类结构的指针,该类方法的链表,该类方法的缓存以及其他必要信息。

[NSObject class];(NSObject 类) 大概返回的就是 Class 中的 isa;

objc 对象分为三层:

instance -> class -> metaclass

(箭头代表 isa 指向,最终 isa 指向 root metaclass)

当然,继承关系后两层是并行的,比如 A B 对象,A 类继承 B 类,则 A 类的 metaclass 继承 B 类的 metaclass.

/// Represents an instance of a class.
struct objc_object {
    Class isa;
};

/// A pointer to an instance of a class.
typedef struct objc_object *id;

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

struct objc_class {
    Class isa;
    
    /* 以下是 Objc 1.0代码, 可能2.0 的实现不同吧 */
    Class super_class;                      /*父类*/
    const char *name;                       /*类名字*/
    long version;                           /*版本信息*/
    long info;                              /*类信息*/
    long instance_size;                     /*实例大小*/
    struct objc_ivar_list *ivars;           /*实例参数链表*/
    struct objc_method_list **methodLists;  /*方法链表*/
    struct objc_cache *cache;               /*方法缓存*/
    struct objc_protocol_list *protocols;   /*协议链表*/
};
/* Use `Class` instead of `struct objc_class *` */

Method & SEL & IMP

SEL在上面已经介绍了实际上他就是等价于方法的名字

Method就是方法 实际上他包含了SEL和IMP 不同于SEL它是有宿主的,并不是单例

而IMP实际就是方法的真正实现了
如果要做动态方法解析 那么就可以自己作IMP来转换SEL对于的实现

/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;

typedef struct objc_method *Method;

struct objc_method {
    SEL method_name;        /*方法名*/
    char *method_types;     /*各参数和返回值类型*/
    IMP method_imp;         /*指向该方法的具体实现的函数指针*/
} 

struct objc_method_list {
    struct objc_method_list *obsolete;

    int method_count;
#ifdef __LP64__
    int space;
#endif
    /* variable length structure */
    struct objc_method method_list[1];
}

typedef id (*IMP)(id, SEL, ...);

根据前面id 的定义,我们知道 id是一个指向 objc_object 结构体的指针,该结构体只有一个成员isa,所以任何继承自 NSObject 的类对象都可以用id 来指代,因为 NSObject 的第一个成员实例就是isa。

至此,我们就很清楚地知道 IMP 的含义:IMP 是一个函数指针,这个被指向的函数包含一个接收消息的对象id(self 指针), 调用方法的选标 SEL (方法名),以及不定个数的方法参数,并返回一个id。也就是说 IMP 是消息最终调用的执行代码,是方法真正的实现代码 。我们可以像在C语言里面一样使用这个函数指针。

void (*setter)(id, SEL, BOOL);

setter = (void(*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled:)];

for (int i = 0; i < 1000; i++) {
    setter(targetList[i], @selector(setFilled:), YES);
}

ivar & property

ivar就是定义的变量,而property就是属性了
这里要注意的就是取出一个class的ivar/property 用到的类似函数
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
注意到它是copy的,也就是说这块内存是copy 你得自己负责最后去

消息机制

objc_msgSend

id objc_msgSend ( id self, SEL op, ... );

Sends a message with a simple return value to an instance of a class.

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.

该消息函数做了动态绑定所需要的一切工作:

  1. 它首先找到 SEL 对应的方法实现 IMP。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。

查找 IMP 的过程

  1. 首先去该类的方法缓存 (在 objc_class 结构中查看cache) 中查找,如果找到了就返回它;

动态方法决议 && 消息转发

当调用一个 NSObject 对象不存在的方法时,并不会马上抛出异常,而是会经过多层转发,层层调用对象的 -resolveInstanceMethod:, -forwardingTargetForSelector:, -methodSignatureForSelector:, -forwardInvocation: 等方法

消息转发流程图
消息转发过程的关键方法

要转发消息给其它对象,forwardInvocation:方法所必须做的有:

  1. 决定将消息转发给谁,并且

forwardInvocation:方法就像一个不能识别的消息的分发中心,将这些消息转发给不同接收对象。或者它也可以象一个运输站将所有的消息都发送给同一个接收对象。它可以将一个消息翻译成另外一个消息,或者简单的"吃掉“某些消息,因此没有响应也没有错误。forwardInvocation:方法也可以对不同的消息提供同样的响应,这一切都取决于方法的具体实现。该方法所提供是将不同的对象链接到消息链的能力。

注意:

  1. forwardInvocation:方法只有在消息接收对象中无法正常响应消息时才会被调用。
  2. 实现forwardInvocation:方法时,不用调用super forwardInvocation:方法,否则,应用仍然会崩溃。

参考

上一篇下一篇

猜你喜欢

热点阅读