Class、isa、元类

栏目: IOS · 发布时间: 5年前

内容简介:本文的所涉及到的源码是我们在学习面向对象的学习中,接触最多的就是类,那么在OC类是由Class类型来表示的,Class是用C的数据结构来表示的。可以看到:

本文的所涉及到的源码是 objc4 源码,截止到写本文最新的是 objc4-750 这个版本。

Class

我们在学习面向对象的学习中,接触最多的就是类,那么在OC类是由Class类型来表示的,Class是用C的数据结构来表示的。

看一下 NSObject 的声明,在头文件中,如下图所示:

Class、isa、元类
@interface NSObject<NSObject>{
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wobjc-interface-ivars"
    Class isa  OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}

可以看到:

1、 NSObject 是实现了 <NSObject> 协议的。

2、 NSObject 中有 Class 类型的 isa 成员变量,外界是无法访问的,另外 isa 指针可能在将来也会被隐藏起来(OBJC_ISA_AVAILABILITY标示了)。

继续看一下 Class 到底是什么?

Class、isa、元类

在上面的文件中可以看到 Class 的定义,如下代码:

typedef struct objc_class *Class;

typedef struct objc_object *id;

可以看出 Class 是一个指向 objc_class 的结构体指针, Objective-C 中的类是由 Class 类型来表示的,它实际上是一个指向 objc_class 结构体的指针。

在下面的头文件中看一下 objc_class 的定义,如下:

Class、isa、元类

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
    
    // ....
}

可以看出, objc_class 用来描述OC中的类,而 objc_object 用来描述OC中的对象,类(objc_class)其实也是一个对象(objc_object),另外 id 是代表对象的,它是指向 objc_object 的结构体指针,它的存在可以让我们实现类似于C++中泛型的一些操作。该类型的对象可以转换为任何一种对象,有点类似于 C语言void * 指针类型的作用。

这里要注意, objc_class 的定义在 objc-runtime-old.h 中和 objc-runtime-new.h 中的不一样。这里以 objc-runtime-new.h 为主,建议可以看看被误解的 objc_class 这篇文章。

再来看一下 objc_object ,如下图所示:

Class、isa、元类

struct objc_object {
private:
    isa_t isa;
// ...
}

objc_object 是一个结构体,里面有个私有成员变量 isaisa_t 类型的。

isa_t 是一个 union 类型的,如下代码:

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

总之在OC中,类也是一个对象称之为类对象,根据凡是对象都有自己的类的原理,那么类对象的肯定存在自己的类,这个类就是元类(meta class)。

元类

在说元类之前,先看一下下面的例子,创建一个 NSMutableDictionary 实例对象 dict ,即向 NSMutableDictionary 发送 allocinit 消息。

NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
```	

上面代码的大概执行流程如下几个步骤:

1、先执行 `[NSMutableDictionary alloc]`,但是 `NSMutableDictionary` 没有 `+alloc` 方法,于是再去父类`NSObject` 中查找该方法。

2、`NSObject` 响应 `+alloc` 方法,开始检测 `NSMutableDictionary` 类,并根据其所需的内存空间大小开始分配内存空间,然后把 `isa` 指针指向 `NSMutableDictionary` 类。同时,`+alloc` 也被加进 cache 列表里面。

3、接着,执行 `-init` 方法,如果 `NSMutableDictionary` 响应该方法,则直接将其加入 `cache`,如果不响应,则去父类查找。

4、在后期的操作中,如果再以 `[[NSMutableDictionary alloc] init]` 这种方式来创建字典对象,则会直接从 cache 中取出相应的方法,直接调用。

上面是创建一个实例对象的大致流程,接下来我们说说元类。

元类简单来说就是类对象的类。类描述的是对象,那么元类描述的就是Class类对象的类。元类定义了类的行为(类方法),在平时开发时,meta class 基本是用不着接触的,但是我们还是要知道它的存在。

```objc
NSMutableDictionary *tDatas = [NSMutableDictionary dictionaryWithCapacity:5];

拿上面的示例来说,向 NSMutableDictionary 发送 dictionaryWithCapacity 这个消息的时候,Runtime 会在这个类的 meta-class 的方法列表中查找,通过 SEL 找到后取出方法中的 IMP 函数入口指针,并执行该方法,如果找不到就进行消息转发的流程中,最终可能会导致 Crash,消息转发的原理和机制可以参考消息机制 这几篇文章。

元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。

Class object_getClass(id obj);

object_getClass 可以获取一个对象的 class object,其源码实现如下:

Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}

举个例子吧,示例如下:

NSObject *obj = [NSObject new];
Class obj1 = object_getClass(obj);
Class obj2 = object_getClass([NSObject class]);
Class obj3 = objc_getMetaClass("NSObject");
Class obj4 = object_getClass(obj1);
const char *name = [NSStringFromClass(obj1) UTF8String];
NSLog(@"name: %s", name); //name: NSObject
Class obj5 = objc_getMetaClass(name);
Class obj6 = objc_getClass(name);
    
NSLog(@"obj : %@, ->%p: ", obj,  obj);
    
NSLog(@"obj1: %@, ->%p: ", obj1, obj1);
NSLog(@"obj2: %@, ->%p: ", obj2, obj2);
NSLog(@"obj3: %@, ->%p: ", obj3, obj3);
NSLog(@"obj4: %@, ->%p: ", obj4, obj4);
NSLog(@"obj5: %@, ->%p: ", obj5, obj5);
NSLog(@"obj6: %@, ->%p: ", obj6, obj6);

打印结果如下:

obj : <NSObject: 0x600002b19d70>, ->0x600002b19d70:
obj1: NSObject, ->0x10c96bf38:
obj2: NSObject, ->0x10c96bee8:
obj3: NSObject, ->0x10c96bee8:
obj4: NSObject, ->0x10c96bee8:
obj5: NSObject, ->0x10c96bee8:
obj6: NSObject, ->0x10c96bf38:

可以看出,obj 是一个实例对象,obj1和obj6是一个 class object,其二者地址也一致,obj2、obj3 和 obj4 都获取到的是元类。

通过类对象调用的 object_getClass 得到的是该类对象的 meta class,如 obj2 和 obj4,而通过实例对象调用的 object_getClass 得到的是该实例对象的类对象,如 obj1, objc_getClass 这个方法获取是实例对象的类对象,与 object_getClass 还是有点不一样的。而 objc_getMetaClass 可以直接获取 meta class,如 obj3。

NSObject.mm 中,可以看到 self 和 class 方法都要实例和类方法,class 方法返回的都是类对象。

+ (id)self 
{
    return (id)self;
}

- (id)self
{
    return self;
}

+ (Class)class 
{
    return self;
}

- (Class)class 
{
    return object_getClass(self);
}

所以,无论是类还是实例调用 class 方法,返回的都是同一个 class object,举例:

Class objClz1 = [NSObject class];
Class objClz2 = [[[NSObject alloc] init] class];
    
if (objClz1 == objClz2) {
    NSLog(@"objClz1: %@, ->%p", objClz1, objClz1);
}

输出结果是:

objClz1: NSObject, ->0x10fa30f38

isa

下面的例子来源自 这里 ,感谢 kingizz’s blog,下面的代码 SonFather 的子类,而 FatherNSObject 的子类。

@interface Father:NSObject

@end
@interface Son:Father

@end

我们结合下面这个图来理解一下,子类、父类、元类以及 isa 指针。

Class、isa、元类

一个实例对象的 isa 指向对象所属的类,这个类的 isa 指向这个类的元类,而这个元类的 isa 又指向 NSObject 的元类, NSObject 的元类的 isa 指向其本身,最终形成形成一个完美的闭环。

在OC中,所有的对象都有一个 isa 指针,指向对象所属的类,类也是一个对象,类对象的 isa 指针指向类的元类。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

C语言程序开发范例宝典

C语言程序开发范例宝典

2010-1 / 59.00元

《C语言程序开发范例宝典》全面介绍了应用C语言进行开发的各种技术和技巧,全书共分12章,内容包括基础知识、指针、数据结构、算法、数学应用、文件操作、库函数应用、图形图像、系统调用、加解密与安全性、游戏、综合应用等。全书共提供300个实例,每个实例都突出了其实用性。 《C语言程序开发范例宝典》既可作为C程序的初学者学习用书,也可作为程序开发人员、相关培训机构老师和学生的参考用书。一起来看看 《C语言程序开发范例宝典》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具