Objective-C 中的类

此文实际成于 2015/07/29

Objc explain Classes and metaclasses

Objective-C 是基于类的对象系统。

  1. 每一个对象都是某一个类的一个实例。

  2. 对象的 isa 指针指向它的类。

  3. 类描述对象的数据信息如:分配的内存大小,ivar类型及布局。

  4. 类同时描述了对象的行为如:它所响应的选择器;它所实现的实例方法。

  5. 类的方法列表是实例方法的集合,对象能够响应选择器。
    当发送一个消息给对象时,objc_msgSend()查询类(或超类,如果有的话)的方法列表,来决定调用哪一个方法。

  6. 每一个 Objective-C 类同样是一个对象。 它有一个isa指针及其他数据,
    同样能响应选择器。当你像这样 [NSObject alloc] 调用一个类方法(class method)时。实际是你是在发送消息给哪一个类对象。

  7. 因为一个类也是一个对象,它就必须是某一个元类(meta class)的实例.

metaclass 是类对象的描述。正如类是普通的实例的描述。
实际上,

  1. 类的方法列表就是类方法列表,类对象响应的选择器。
  2. 当你发送一个消息给一个类(一个元类的实例),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_objectid

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;
};

参考文档

  1. Friday Q&A 2009-03-13: Intro to the Objective-C Runtime
  2. [objc explain] Classes and metaclasses
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,417评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,921评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,850评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,945评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,069评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,188评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,239评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,994评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,409评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,735评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,898评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,578评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,205评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,916评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,156评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,722评论 2 363
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,781评论 2 351

推荐阅读更多精彩内容

  • 你真的知道OC的类(Class)是个什么玩意儿? 众所周知,所有的对象都是由其对应的类实例化而来,但是类本身其实也...
    潭清阅读 552评论 0 0
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,692评论 0 9
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,226评论 1 5
  • 我们都知道,Objective-C是一门基于C语言的面向对象的语言,它的对象模型是基于类来建立的。我们可以在苹果开...
    Tamp_阅读 1,532评论 1 13
  • 今天是假期的最后一天,晨起有点着凉,迷迷糊糊地睡了一上午。临近11点,被老公喊起,他炒菜,我洗衣。午饭是母亲早已煮...
    花儿朵朵开7阅读 455评论 1 5