Class、isa、元类

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

内容简介:本文的所涉及到的源码是我们在学习面向对象的学习中,接触最多的就是类,那么在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 指针指向类的元类。


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

查看所有标签

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

The Haskell School of Expression

The Haskell School of Expression

Paul Hudak / Cambridge University Press / 2000-01 / USD 95.00

Functional programming is a style of programming that emphasizes the use of functions (in contrast to object-oriented programming, which emphasizes the use of objects). It has become popular in recen......一起来看看 《The Haskell School of Expression》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具