几个基本的概念:
id,
id的定义是,
typedef struct objc_object {
Class isa;
} *id;
id是指向struct objc_object的一个指针。
这个意思是,id是一个指向任何一个继承了NSObject的对象,可以指向任意一个NSObject子类的对象。
需要注意的是id是一个指针,所以在使用id的时候不需要加星号。
SEL
SEL的定义是,
typedef struct objc_selector *SEL;
是指向 struct objc_selector的指针,而 objc_selector 的定义并没有在runtime.h中给出定义。
Objective-C在编译时,会根据方法的名字生成一个用来区分这个方法的唯一的一个ID。只要方法名称相同,那么它们的ID就是相同的。
说白了SEL是一个方法的方法名。
不同类的实例对象执行相同的selector时,会在各自的方法列表中去根据selector去寻找自己对应的IMP。
IMP
IMP的定义是,
typedef id (*IMP)(id, SEL, …);
为方法具体实现代码块的地址,可像普通C函数一样调用。
是指向一个函数的一个指针。这个被指向的函数包括一个id(标识是哪个instance或者class的方法),一个SEL(标识是哪个方法),以及函数的入参以及返回值。
说白了,IMP指向实现代码。
Method
Method,也就是我们说的方法,
在objc/objc-class.h中是这样定义的:
struct objc_method
{
SEL method_name;
char * method_types;
IMP method_imp;
};
一个方法Method,包含了一个方法名SEL,一个指向方法实现地址的指针IMP,以及入参和出参的描述字符串。
==
iOS的实例,对象,元类
参考唐巧的这篇博客:http://blog.devtang.com/2013/10/15/objective-c-object-model/
实例
instance of class的内存结构里存储了isa指针,以及instance的成员变量。
类的实例看成一个 C 语言的结构体(struct),上面说的 isa 指针就是这个结构体的第一个成员变量,而类的其它成员变量依次排列在结构体中。
isa指向实例的类对象Class。Class中又存储了Class的isa 和super Class指针,以及实例的实例方法等其他list指针,Class的isa指向元类。元类的isa指向根元类,根元类的isa指向自身,这样就形成了isa指针的闭环。也形成了属性和方法存储和查找的闭环。
因为对象在内存中的排布可以看成一个结构体,该结构体的大小并不能动态变化。所以无法在运行时动态给对象增加成员变量。
相对的,对象的方法定义都保存在类的可变区域中。方法的定义列表是一个名为 methodLists
的指针的指针(如下图所示)。通过修改该指针指向的指针的值,就可以实现动态地为某一个类增加成员方法。这也是Category
实现的原理。同时也说明了为什么Category
只可为对象增加成员方法,却不能增加成员变量。
**:特别说明一下,通过objc_setAssociatedObject
和 objc_getAssociatedObject
方法可以变相地给对象增加成员变量,但由于实现机制不一样,所以并不是真正改变了对象的内存结构。
Class
每一个对象都是一个类的实例。在 Objective-C 语言的内部,每一个对象都有一个名为 isa 的指针,指向该对象的类。每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。每一个对象都可以接受消息,而对象能够接收的消息列表是保存在它所对应的类中。
Meta-Class
因为类也是一个对象,那它也必须是另一个类的实列,这个类就是元类 (metaclass
)。元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有,则该元类会向它的父类查找该方法,直到一直找到继承链的头。
这三部分的闭环结构见上。