首页 > 代码库 > Objective-C 对象模式深入分析

Objective-C 对象模式深入分析

The Objective-C object model
作为一名Objective-C的软件开发者,你可以在不需要理解Objective-C的鼻祖C语言和Objective-C所采用的对象模型的情况下,依然能够完成许多的工作。事实上,因为这些特性的确很复杂,要去学习这些个对象模型可能会令你感到望而却步。
虽然这个问题的确比较复杂,但是还是有努力去学习它的必要性的,因为你最终还是会需要和Objective-C运行时的情况打交道的,而那个时候理解了对象模型将会帮助你更好地解决此类问题。
端起一杯茶,或者一杯咖啡,跟着我一起来探索Objective-C的对象模型吧。

对象是类的实例

看起来这个是很明显的,但是我们不妨花些时间来认真思考一下当我们说“一个对象是一个类的实例”的时候,这句话意味着意味着什么。以下面的代码举例说明。
NSString *name = [NSString stringWithFormat:@"%@", @"Peter"];
这个代码看起来更像是人造的,而不是实际使用需要的一段代码,但是它对于展示Objective-C运行时的具体工作方式是很重要的。当上述代码执行完毕后,我们最终获取到的是一个叫做name的变量,该变量是NSString类的一个实例,请细想一下以下知识:
  • 对象(类的实例)是用来保存状态的
  • 一个对象的状态保存在它的实例变量内。比如,上述代码中的name变量分配了一些内存用来保存字符串内容"Peter"
  • name变量同时也包含了一个指向它所在的类(NSString)的指针
  • Objective-C里面,对象不包含任何方法(行为),仅仅保存着状态信息
  • 类只包含方法,不包含任何状态信息。这些包含的方法是他们的实例能够响应的方法(即实例方法)
  • 为了使得我们清楚一些,我们再重复一遍:对象包含了保存的状态(即实例变量),而类包含了保存的行为(方法)
为了使得表述更加具体,我们来看看下面的图标:
技术分享
实例方法图
上图中蓝色的框框表示name变量(类NSString的一个实例)。根据上图所示,让我们来看一下,如果你向变量name发送length的消息,那么会发生什么?
NSUInteger len = [name length];
  1. 首先Objective-C会询问变量name是哪个类的实例,因为所有的方法都包含在类里面,所以从那里面才能开始寻找方法length在哪。
  2. 因为name是类NSString的实例变量,所以Objective-C将会先找到类NSString,然后开始查找方法length
  3. 如果Objective-C在类NSString里面找到了方法length,那么它就会停止查找并开始执行方法。否则,它就会根据类的继承关系继续查找。
需要说明一点的是,上面所描述的过程是经过我的简化的,我没有考虑方法存储的其他情况。(比如Objective-C的分类)


类也是对象

既然类能够保存它的实例的方法(实例方法),那么类方法保存在哪里呢?我们再来看看创建变量name的代码:
NSString *name = [NSString stringWithFormat:@"%@", @"Peter"];
上述代码中,消息stringWithFormat:是向类NSString发送的。类可以接收消息吗?如果可以,那么那些方法保存在哪里呢?事实上是这样的,Objective-C的语法隐藏了一些东西,他隐藏了类也是对象的事实。
如果类NSString是一个对象,那就意味着它一定是从一个保存着它的方法的类实例化来的。这个猜测是对的,不过在深入这个问题之前,我们再来看一段代码:
@interface Person : NSObject {}
+ (void) aClassMethod;
- (void) anInstanceMethod;
@end
也许你看过这种类似的代码很多很多,但是这这段代码干嘛的?
  • 声明了一个类,该类是Person
  • 这个类用来保存实例方法
  • 在这段代码的背后也创建了另外的一个类,不幸的是这个额外的类名也是Person,只不过它是Person元类而已
  • Person元类会保存着Person的类方法
看起来现在的问题越来越复杂了,因为相同的名字用了一遍又一遍,回到最初的变量name上来讨论,我们可以说:
  • 变量name是类NSString的一个实例
  • NSString是一个NSString元类实例化的一个对象
  • 如果你向对象name发送消息length,那么Objective-C会去类NSString查找这个方法
  • 如果你向对象NSString发送stringWithFormate:消息,Objective-C会在NSString元类里面查找这个方法
  • NSString是一个类,也是一个对象,类也是一个对象
这里是另外一个类图示意图
技术分享
元类方法(类方法)图
这一次,蓝色的框框表示的是NSString对象,我们再来看看创建name对象的代码:
NSString *name = [NSString stringWithFormat:@"%@", @"Peter"];
当向对象NSString发送消息stringWithFormate:的时候,Objective-C将会:
  1. 询问NSString对象是哪个类的一个实例,哪一个是它的元类
  2. 开始在NSString的元类里面查找方法stringWithFormate:,如果没找到,它会继续根据类的类的继承关系继续查找
当然,因为NSString继承自NSObject,所以NSString元类继承自NSObject元类。

奇怪的NSObject继承

如果到了这里你还算清醒的话,那么利用你现在所掌握的关于Objective-C对象模型的知识够你去了解你的代码在运行时会发生了什么。可能还有一个问题你想知道:为了么NSObject元类好像继承自NSObject类?这是因为本来就是这样子的。
考虑一下以下情况:
  • NSObject为它的实例保存着方法(保存实例方法)
  • NSObject元类为类NSObject保存着方法。(保存类方法)
  • NSObject元类继承自非元类NSObject
  • 因此,NSObject的实例方法也是类方法,这些方法在所有Objective-C的元类方法的查找路径中
有点奇怪,但是如果你再去看看上面的图,你就应该会更清楚些了。

Objective-C 对象模式深入分析