内容简介:本文的所涉及到的源码是我们在学习面向对象的学习中,接触最多的就是类,那么在OC类是由Class类型来表示的,Class是用C的数据结构来表示的。可以看到:
本文的所涉及到的源码是 objc4 源码,截止到写本文最新的是 objc4-750
这个版本。
Class
我们在学习面向对象的学习中,接触最多的就是类,那么在OC类是由Class类型来表示的,Class是用C的数据结构来表示的。
看一下 NSObject
的声明,在头文件中,如下图所示:
@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
的定义,如下代码:
typedef struct objc_class *Class; typedef struct objc_object *id;
可以看出 Class
是一个指向 objc_class
的结构体指针, Objective-C
中的类是由 Class
类型来表示的,它实际上是一个指向 objc_class
结构体的指针。
在下面的头文件中看一下 objc_class
的定义,如下:
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
,如下图所示:
struct objc_object { private: isa_t isa; // ... }
objc_object
是一个结构体,里面有个私有成员变量 isa
是 isa_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
发送 alloc
和 init
消息。
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,下面的代码 Son
是 Father
的子类,而 Father
是 NSObject
的子类。
@interface Father:NSObject @end
@interface Son:Father @end
我们结合下面这个图来理解一下,子类、父类、元类以及 isa 指针。
一个实例对象的 isa
指向对象所属的类,这个类的 isa
指向这个类的元类,而这个元类的 isa
又指向 NSObject
的元类, NSObject
的元类的 isa
指向其本身,最终形成形成一个完美的闭环。
在OC中,所有的对象都有一个 isa
指针,指向对象所属的类,类也是一个对象,类对象的 isa
指针指向类的元类。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C语言程序开发范例宝典
2010-1 / 59.00元
《C语言程序开发范例宝典》全面介绍了应用C语言进行开发的各种技术和技巧,全书共分12章,内容包括基础知识、指针、数据结构、算法、数学应用、文件操作、库函数应用、图形图像、系统调用、加解密与安全性、游戏、综合应用等。全书共提供300个实例,每个实例都突出了其实用性。 《C语言程序开发范例宝典》既可作为C程序的初学者学习用书,也可作为程序开发人员、相关培训机构老师和学生的参考用书。一起来看看 《C语言程序开发范例宝典》 这本书的介绍吧!