此文实际成于 2015/07/29
Objc explain Classes and metaclasses
Objective-C 是基于类的对象系统。
-
每一个对象都是某一个类的一个实例。
-
对象的
isa
指针指向它的类。 -
类描述对象的数据信息如:分配的内存大小,
ivar
类型及布局。 -
类同时描述了对象的行为如:它所响应的选择器;它所实现的实例方法。
-
类的方法列表是实例方法的集合,对象能够响应选择器。
当发送一个消息给对象时,objc_msgSend()
查询类(或超类,如果有的话)的方法列表,来决定调用哪一个方法。 -
每一个 Objective-C 类同样是一个对象。 它有一个
isa
指针及其他数据,
同样能响应选择器。当你像这样[NSObject alloc]
调用一个类方法(class method)时。实际是你是在发送消息给哪一个类对象。 -
因为一个类也是一个对象,它就必须是某一个元类(meta class)的实例.
metaclass 是类对象的描述。正如类是普通的实例的描述。
实际上,
- 类的方法列表就是类方法列表,类对象响应的选择器。
- 当你发送一个消息给一个类(一个元类的实例),
objc_msgSend()
查找元类(或其超类,如果有的话)的方法列表来决定调用哪一个方法。
正如实例方法以类来描述以代表类对象。类方法以元类来描述来代表实例对象。
什么是元类?
元类是根类的元类的实例。 根元类本身是根根元类的一个实例。isa
链在这里以一个循环结尾: 实例到类对元类到根类到它自身。元类的 isa
指针的行为一般不影响什么的。因为在现实中没有人发送信息给元类对象。
最重要的是一个元类的超类。元类的超类链跟类的越类链是并行的,所以类方法的继承也跟实例方法的继承是并行的。并且根元类的越类是根类,因此每一个类对象响应根类的实例方法。
最后,类对象是根类的或其子类的实例,正如其他对象一个。
如下图:
objc class and meta class
对应代码如下:
// Connect to superclasses and metaclasses
cls->initClassIsa(meta);
if (superclass) {
meta->initClassIsa(superclass->ISA()->ISA());
cls->superclass = superclass;
meta->superclass = superclass->ISA();
addSubclass(superclass, cls);
addSubclass(superclass->ISA(), meta);
} else {
meta->initClassIsa(meta);
cls->superclass = Nil;
meta->superclass = cls;
addSubclass(cls, meta);
}
上面的 对于isa
的设置主要在 cls->initClassIsa(meta)
方法中体现 。
inline void
objc_object::initClassIsa(Class cls)
{
initIsa(cls);
}
然后很多与初始化isa
操作,最后都调用到了initIsa(cls)
方法上来。
对于非 Tagged Pointer 的 ISA 类型,
其初始 化操作就是一个简单的赋值操作:
inline void
objc_object::initIsa(Class cls)
{
assert(!isTaggedPointer());
isa = (uintptr_t)cls;
}
对于 Tagged Pointer 实现的
其实现稍微复杂一些,如下 :
inline void
objc_object::initIsa(Class cls)
{
initIsa(cls, false, false);
}
inline void
objc_object::initClassIsa(Class cls)
{
if (DisableIndexedIsa) {
initIsa(cls, false, false);
} else {
initIsa(cls, true, false);
}
}
inline void
objc_object::initProtocolIsa(Class cls)
{
return initClassIsa(cls);
}
inline void
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
assert(!UseGC);
assert(!cls->requiresRawIsa());
assert(hasCxxDtor == cls->hasCxxDtor());
initIsa(cls, true, hasCxxDtor);
}
inline void
objc_object::initIsa(Class cls, bool indexed, bool hasCxxDtor)
{
assert(!isTaggedPointer());
if (!indexed) {
isa.cls = cls;
} else {
assert(!DisableIndexedIsa);
isa.bits = ISA_MAGIC_VALUE;
// isa.magic is part of ISA_MAGIC_VALUE
// isa.indexed is part of ISA_MAGIC_VALUE
isa.has_cxx_dtor = hasCxxDtor;
isa.shiftcls = (uintptr_t)cls >> 3;
}
}
小结
当像一个对象发送消息时,方法的查找以对象的isa
指针开始,然后继续到其超类链。
实例方法在类中定义
类方法在元类定义加上根类(非元类)。
类方法与实例方法的查找
按上面的说法,类方法其实是类对象isa
所指向的元类的实例方法。
因此其实现如下 :
/***********************************************************************
* class_getClassMethod. Return the class method for the specified
* class and selector.
**********************************************************************/
Method class_getClassMethod(Class cls, SEL sel)
{
if (!cls || !sel) return nil;
return class_getInstanceMethod(cls->getMeta(), sel);
}
新类的内存分配
/***********************************************************************
* objc_allocateClassPair
* fixme
* Locking: acquires runtimeLock
**********************************************************************/
Class objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
{
Class cls, meta;
rwlock_write(&runtimeLock);
// Fail if the class name is in use.
// Fail if the superclass isn't kosher.
if (getClass(name) || !verifySuperclass(superclass, true/*rootOK*/)) {
rwlock_unlock_write(&runtimeLock);
return nil;
}
// Allocate new classes.
cls = alloc_class_for_subclass(superclass, extraBytes);
meta = alloc_class_for_subclass(superclass, extraBytes);
// fixme mangle the name if it looks swift-y?
objc_initializeClassPair_internal(superclass, name, cls, meta);
rwlock_unlock_write(&runtimeLock);
return cls;
}
从
cls = alloc_class_for_subclass(superclass, extraBytes);
meta = alloc_class_for_subclass(superclass, extraBytes);
可以看出,类和元类的继承是并行的。
整个类复杂的关联就在下面这大段代码中体现:
/***********************************************************************
* objc_initializeClassPair
* Locking: runtimeLock must be write-locked by the caller
**********************************************************************/
// &UnsetLayout is the default ivar layout during class construction
static const uint8_t UnsetLayout = 0;
static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta)
{
rwlock_assert_writing(&runtimeLock);
class_ro_t *cls_ro_w, *meta_ro_w;
cls->cache.setEmpty();
meta->cache.setEmpty();
cls->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
meta->setData((class_rw_t *)_calloc_internal(sizeof(class_rw_t), 1));
cls_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
meta_ro_w = (class_ro_t *)_calloc_internal(sizeof(class_ro_t), 1);
cls->data()->ro = cls_ro_w;
meta->data()->ro = meta_ro_w;
// Set basic info
cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
cls->data()->version = 0;
meta->data()->version = 7;
cls_ro_w->flags = 0;
meta_ro_w->flags = RO_META;
if (!superclass) {
cls_ro_w->flags |= RO_ROOT;
meta_ro_w->flags |= RO_ROOT;
}
if (superclass) {
cls_ro_w->instanceStart = superclass->unalignedInstanceSize();
meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize();
cls->setInstanceSize(cls_ro_w->instanceStart);
meta->setInstanceSize(meta_ro_w->instanceStart);
} else {
cls_ro_w->instanceStart = 0;
meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class);
cls->setInstanceSize((uint32_t)sizeof(id)); // just an isa
meta->setInstanceSize(meta_ro_w->instanceStart);
}
cls_ro_w->name = _strdup_internal(name);
meta_ro_w->name = _strdup_internal(name);
cls_ro_w->ivarLayout = &UnsetLayout;
cls_ro_w->weakIvarLayout = &UnsetLayout;
// Connect to superclasses and metaclasses
cls->initClassIsa(meta);
if (superclass) {
meta->initClassIsa(superclass->ISA()->ISA());
cls->superclass = superclass;
meta->superclass = superclass->ISA();
addSubclass(superclass, cls);
addSubclass(superclass->ISA(), meta);
} else {
meta->initClassIsa(meta);
cls->superclass = Nil;
meta->superclass = cls;
addSubclass(cls, meta);
}
}
Objective-C 的类是以 struct objc_class
来实现的。
而其继承自 struct objc_object
即 id
struct objc_class
有新旧两种定义。老的定义虽然很 OS X 10.5 就不再用了。但是有助于帮助我们理解 struct objc_class
有什么 。
类的老的运行时定义
老的定义在 objc-runtime-old.h
中。
struct objc_class : objc_object {
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
上面的 Class
类型,是:
typedef struct objc_class *Class
它们都在 objc-private.h
中有定义:
struct objc_class;
struct objc_object;
typedef struct objc_class *Class;
typedef struct objc_object *id;
也就是说objc_class
实现了一种单链表的结构。也可以说它是类的单继承结构。
Class
好比是链表结构中的一个Node
从上面的结构定义可以看出老式的类定义有哪些内容呢?
- `Class superclass` 其指向的超类
- `const char *name` 类名
- `version` 类的版本
- `info` 类信息
- `instance_size` 类对象实例的大小
- `struct objc_ivar_list *ivars` 实例变量列表
- `struct objc_method_list **methodLists` 实例方法列表
- `struct objc_cache *cache` 方法解析缓存的Hash 表
- `struct objc_protocol_list *protocols` 类实例实现的协议列表
也不能忘了其公开的头文件的声明 `runtime.h`
中将`isa`字段也包括在内。但是实现是在`struct objc_object`中声明的。
```c
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
}
```
`isa` 表明一个实际的对象实际上是一个什么类。实际上每一个 objc 的对象都 是一个一个`isa`指针开始的,要不 objc 运行时不知道怎么处理这一块内存,这一个对象。
新的类定义
其基本声明如下 :
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
class_rw_t *data() {
return bits.data();
}
}
新的实现更复杂高效。
上面介绍的实例变量列表,方法列表,协议列表都由 class_rw_t
结构来实现了。
其声明如下 :
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
union {
method_list_t **method_lists; // RW_METHOD_ARRAY == 1
method_list_t *method_list; // RW_METHOD_ARRAY == 0
};
struct chained_property_list *properties;
const protocol_list_t ** protocols;
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
其中 const class_ro_t *ro
; 存储了方法列表,协议列表,实例变量列表的关键信息。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
const method_list_t * baseMethods;
const protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
const property_list_t *baseProperties;
};
网友评论