理解 “类对象” 的用意
类对象并非在编译器就绑定好了,而是在运行期查找。
Objective-C的对象的本质是什么?
每个Objective-C的对象实例都是指向某块内存数据的指针。所以声明时,类型后面要跟一个 “*” 号
|
|
编过C语言的人都知道这是什么意思,对于没有写过C语言的程序员来说,pointerVarible可以理解成为存放内存地址的变量,而NSString自身的数据就存在与那个地址中。因此说可以说,pointerVarible改变量“指向”(point)NSString实例。所有Objective-C对象都是如此,若是想把对象所需的内存分配到栈上,编译器则会报错
|
|
对于通用的对象类型id,由于其本身已经是指针了,所以我们能够这样写:
|
|
上面这种定义方式和NSString* 来定义相比,其语法意义相同。唯一的区别在于,如果声明时指定了具体类型
,那么在该类实例上调用其所没有的方法时,编译器会探知此情况,并发出警告信息。
描述Objective-C对象所用的数据结构定义在运行期程序库的头文件里,id本身也定义在这里:
|
|
由此可见,每个对象的首个成员是Class类的变量。改变量定义了对象所属的类,同常称之为“isa”指针。例如刚才的例子中的对象
“是一个”(isa)NSString,所以其“isa”指针就指向了NSString。Class对象也定义在运行期程序库的头文件中:
|
|
此结构存放类的”元数据“,例如类的实例实现了几个方法,具备多少个实例变量的信息。此结构的首个变量也是isa指针,这说明Class本事亦为Objective-C对象。
结构体里还有个变量叫做 super_class ,他定义了本类的超类。类对象所属的类型(也就是isa指针所指向的类型)是另外一个类,叫做“元类”,用来描述对象本身
所具备的“元数据”。类方法就定义在此处,因为这些方法可以理解为类对象的实例方法。没个类仅仅有一个“类对象”,而每个”类对象“仅有一个与之相关的“元类”。
假设有个名为someClass的子类从NSObject中继承而来,则其继承体系如图

SomeClass 实例所属 “类继承体系” ,此类继承自NSObject,图中也画出了两个“元类”之间的继承关系。
super_class 指针确立了继承关系, 而 isa 指针描述了实例所属的类。通过这张布局关系图,即可执行“类型信息查询”。我们可以查出对象能否响应某个选择子,
是否遵从某项协议,并且能够看出此对象位于 “类继承体系”的哪一部分。
在类继承体系中查询类型信息
可以用类型信息查询方法来检视类继承体系。“isMemberOfClass:”能够判断出对象是否为某个特定类的实例, 而“isKindOfClass:”能够判断出对象是否为某类或是
其派生类的实例。 例如:
|
|
像这样类型信息查询方法使用isa指针获取对象所属的类,然后通过super_class指针在继承体系中游走。由于对象是动态的,所以此特性显得极为重要。Objective-C与你可能熟悉的其他语言不同,在此语言中,必须查询类型信息,方能完全了解对象的真实类型。
由于Objective-C使用 “动态类型系统”,所有用查询对象所属类的类型信息查询功能非常有用。从collection获取对象时,通常也会查询类型信息。这些对象不是强类型
的,把它们从collection中取出来时,其类型通常为id,如果想知道具体类型,那就要使用类型信息查询方法。例如:想根据数组中存储的对象生成以逗号分隔的字符串,并保存至文本文件,就可以使用一下代码:
也可以用比较类对象是否等同的办法来做。若是如此,那就要使用==操作符,而不要使用比较Objective-C对象时常用的“isEqual:”方法.原因在于,类对象是“单例”,在应用程序范围内,每个类的Class仅有一个实例,也就是说,另外一种可以精确判断出对象是否为某实例的办法是:
|
|
即便能这样做,我们也应该尽量使用类型信息查询方法,而不应该直接比较两个类对象是否等同,因为前者可以正确处理哪里使用了消息传递机制的对象。比方说,某个对象可能会把其收到的所有选择子都转发给另一个对象,这样的对象叫做 “代理”, 此种对象均以NSPeoxy为根类。
通常情况下,如果此种代理对象上调用class方法,那么返回的是代理对象本身(此类是NSProxy的子类),而非接受代理的对象所属的类。然而,若是改用 “isKindOfClass:”这样的类型信息查询方法,那么代理对象就会把这条信息转给“接受代理的对象”。也就是说,这条信息的返回值与直接在接受代理的对象上面查询其
类型所得到结果相同。因此,这样查询出来的类对象与通过class方法返回的那个类对象不同。class方法所返回的类表示发起代理的对象,而非接手代理的对象。
要点
每个实例都有一个指向Class对象的指针,用以表明其类型,而这些Class对象则构成了类的继承体系
如果对象类型无法再编译期确定,那么久应该使用类型信息查询方法来探知。
尽量使用类型信息查询方法来确定对象类型,而不要直接比较类对象,因为某些对象可能实现了消息转发功能。