转:(via:放慢脚步的博客)
对于NSObject来说,我们可是非常熟悉的了,它是objc中大多数类的基类。注意,并不是所有的类都继承于NSObject,比如NSProxy类。详细的类图可以参考这篇文章
IOS 整体框架类图,讲的很详细。
既然要深入学习objc,我们不妨从最熟悉的NSObject类开始,让我们来看一看它的源码吧!
首先,我们来看看NSObject.h这个头文件,我们很容易从Xcode中进入这个文件中。它被包含在Foundation/NSObject.h路径下,如下图所示:
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114022_1.png)
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114040_1.png)
看了这个头文件,我们就有点疑惑了,怎么这个头文件了没有NSObject类的定义啊,只是定义了@interface NSObject (NSCoderMethods) 和 @protocol NSCopying 等几个类别和协议。那么真正的NSObject的类定义在哪里呢?
是不是使用#import从其他文件中引入的呢?看看这个文件的第6行代码,果然发现了我们想找到的头文件定义。
- #import
我们找到这个头文件,如下图所示:
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114129_1.png)
终于,我们找到了如下所示的NSObject的类定义:
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114156_1.png)
看到了我们熟悉的alloc、init等方法。但是,下面这个结构我们有点陌生,NSObject只有一个成员变量是Class类型的isa变量。那么Class类型又是什么东西呢?看命名应该是指所属的类。
- @interface NSObject {
- Class isa;
- }
我们进入Class中看看它到底是个什么东西。
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114237_1.png)
我们发现Class就是一个结构体的指针而已。
- typedef struct objc_class *Class;
我们还需要在看看objc_class是个什么东西。
![](http://www.cocoachina.com/cms/uploads/allimg/140715/8370_140715114314_1.png)
- struct objc_class {
- Class isa OBJC_ISA_AVAILABILITY;
- #if !__OBJC2__
- Class super_class OBJC2_UNAVAILABLE;
- const char *name OBJC2_UNAVAILABLE;
- long version OBJC2_UNAVAILABLE;
- long info OBJC2_UNAVAILABLE;
- long instance_size OBJC2_UNAVAILABLE;
- struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
- struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
- struct objc_cache *cache OBJC2_UNAVAILABLE;
- struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
- #endif
- } OBJC2_UNAVAILABLE;
- /* Use `Class` instead of `struct objc_class *` */
可以看出来对于objc2和objc1的实现是不同的,我们现在只看objc2.0对应的版本,我们把objc 1.0的代码删掉,就只剩下如下结构了:
- struct objc_class {
- Class isa;
- };
真的是好简洁啊!
让我们再来对比一下NSObject的定义:
- @interface NSObject {
- Class isa;
- }
我靠,这俩货怎么长的这么像啊!
有点乱,别急,我们整理一下。
首先,我们知道了Class类型就是objc_class结构体的指针,如下:
- typedef struct objc_class *Class;
而objc_class又如下定义,包含了一个指向自身类型的指针的变量isa。
- struct objc_class {
- Class isa;
- };
我们都知道objc中id也是一种对象类型,那么它究竟是什么呢?来看下面的定义:
- typedef struct objc_object {
- Class isa;
- } *id;
原来id类型就是一个objc_object结构体的指针啊,这个objc_object的结构也仅仅包含一个isa变量。上述我们讨论的关键的几个数据结构NSObject,objc_class,objc_object都仅有一个objc_class * 类型,也就是Class类型的变量isa,可见isa这个变量是多么的重要啊。
我们可以得出这样的结论,在objc的runtime中,类是用objc_class结构体表示的,对象是用objc_object结构体表示的。 对象的isa用来标示这个对象是哪个类的实例。
下面我们来验证这一点,首先我们来看一段代码:
- NSString *str = @"hello world";
- if ([str isMemberOfClass:[NSString class]]) {
- // str is actual an instance of NSString
- } else {
- // str is not a NSString instance
- }
这是我们在实际的开发过程中经常用到的,用来判断str变量是不是NSString类型的一个实例。我们都知道- (BOOL)isMemberOfClass:(Class)aClass可以判断对象是不是某个类的实例。我们来看一下这个方法的源码是如何实现的。
首先,这些源码是属于objc runtime的,庆幸的是objc runtime的源代码苹果已经开源了,你可以在这里下载到
objc的runtime源代码。打开objc/Source/NSObject.mm这个文件,这就是NSObject的实现代码。大概浏览一遍,函数是不是有很多都非常熟悉呢?可以说这些函数接口我们每天都在用,这次终于该好好看看它们是怎么实现的了。
为了方便,我把其中一部分代码摘录出来,并把其中用到 Tagged Pointer 支持的代码去掉,关于 Tagged Pointer 我会单独写一篇文章来分析它,这里我们先忽略即可。
- - (BOOL)isMemberOfClass:(Class)cls {
- return [self class] == cls;
- }
- - (Class)class {
- return object_getClass(self);
- }
- Class object_getClass(id obj)
- {
- return _object_getClass(obj);
- }
- static inline Class _object_getClass(id obj)
- {
- if (obj) return obj->isa;
- else return Nil;
- }
上面的代码非常直白,无需多言。我们注意下最后这个函数:
- static inline Class _object_getClass(id obj)
- {
- if (obj) return obj->isa;
- else return Nil;
- }
如果obj不空,返回的Class类型就是obj->isa,否则,返回的是Nil。这就从源码上证实了isa就是代表一个对象的类型。
通过上面的源码分析,我们搞明白了对于普通的对象实例,isa就是表明它所属的类型。通过调用[obj1 class]等方法返回的其实就是这个isa变量。
下面我们再来分析一下类objc_class,为什么它也有一个isa变量呢?
- struct objc_class {
- Class isa;
- };
在面向对象的设计中,万物皆对象,所以类(Class)也是一个对象,它也有一个isa,那么类的isa指向谁呢?
我们看这句代码[NSObject class],这里我们调用了+ (Class)class这个类方法,我们再开发中经常用到这个方法,它返回的是这个类所属的Class类型。这是不是就可以作为一个类也是一个对象的佐证呢?
我们继续看看objc runtime的源码,你可以在objc/Source/NSObject.mm这个文件中找到下面的代码:
- + (Class)class {
- return self;
- }
是不是有点看不懂了?[NSObject class]这句代码返回的竟然是self,我们都知道对于一个类的实例来说,self总是指向其自身的,但是这里并没有实例啊,我们是直接调用了NSObject这个类的一个类方法+(Class)class啊。难道类本身也有一个self指针?
别急,我们再来仔细看看类方法+(Class)class的文档:
Returns the class object.
(Class)class Return Value The class object.
这个函数的返回值是一个类对象class object,所以其本质上还是一个对象而已。既然是一个对象,它拥有一个self指针是不是就不奇怪了呢?现在我们就明白了,对于像NSObject这样的类来说,它其实代表的是一个类对象,本质上还是一个普通的实例对象,类对象的self指针应该指向的是这个类对象自身。
是不是有点明白了?
但是,那这个类对象又是谁的实例呢?很遗憾,要找到这个问题的答案,我们在 objc runtime 这一层上已经没办法办到了,我们需要到更低层,也就是 objc 语言层去寻找答案了。因为self是 objc 的保留关键字,所以,objc 需要为这个关键字提供语言层面上的支持,我们要找到答案,可以从self这条线索入手,看看 objc 到底是怎样实现的?
感兴趣的同学可以研究下
clang的源码,是用C++实现的,objc 的语言层就是用它来生成的,在clang/lib/CodeGen目录下有关于objc代码生成相关的代码,关于self到底是什么,我觉着可以从LoadObjCSelf这个函数入手去研究一下。
总结:本篇文章只是从我们经常用到的一些函数的源码出发,引出了对 objc 对象模型的一些思考,但是,并没有解释的非常清楚。我也是初次接触这块的东西,我会继续把我学习的一些心得体会记录下来,欢迎大家指正与讨论。下一篇文章我希望继续把 objc meta class 相关的内容与大家一起分享。
是不是觉着还不过瘾?那么请看看下面这些大牛的文章吧。