由于每个Objective-C类都继承自NSObject,因此我们有必要花些时间了解一下NSObject。NSObject的构造方式很复杂:
·它定义了一些本地类方法与实例方法,主要与实例化基本功能及方法发送和解析相关。(参见NSObject Class Reference。)
·它使用了NSObject协议。该协议声明了与内存管理相关的实例方法、实例与类之间的关系以及内省机制。由于所有的NSObject协议方法都是必需的,NSObject类会将其全部实现出来。(参见NSObject Protocol Reference。)在Swift中,NSObject协议叫作NSObjectProtocol,目的在于避免命名冲突。
·它实现了与NSCopying、NSMutableCopying与NSCoding协议相关的便捷方法,但却没有正式使用这些协议。NSObject有意不使用这些协议的原因在于这会导致所有其他的类都要使用该协议,但这么做是错误的。多亏了该架构,如果某个类使用了其中一个协议,那么你就可以调用相应的便捷方法。比如,NSObject实现了copy实例方法,这样你就可以在任何实例上调用copy了,但如果实例所属的类没有使用NSCopying协议并实现copyWithZone:,那就会导致应用崩溃。
·有大量方法通过NSObject上的20多个类别注入了NSObject中,它们散落在各种头文件中。比如,awakeFromNib(参见第7章)来自于NSObject上的UINibLoadingAdditions类别,它声明在UINibLoading.h中。
·class对象也是个对象。因此,所有Objective-C类(Class类型的对象)都继承自NSObject。这样,NSObject所定义的任何实例方法都能以类方法的形式在class对象上调用!比如,respondsToSelector:是NSObject定义的一个实例方法,但也可以将其看作类方法并发送给class对象。
程序员所面临的一个问题就是Apple的文档在分类上过于死板。如果想了解某个对象,那么就不要管该对象的方法来自于何处;只要知道方法是什么就行。但Apple通过来源对方法进行了区分。虽然NSObject是根类,也是最重要的类,所有其他的类都继承自它,但没有一页文档概述了所有这些方法。相反,你需要同时查询NSObject Class Reference与NSObject Protocol Reference,还有NSCopying、NSMutableCopying与NSCoding协议的文档页(为了理解它们如何与NSObject定义的方法进行交互),你还要提供每个NSObject实例方法的类方法版本!
还有一些方法通过类别注入了NSObject中。一些通用方法会在NSObject类文档页面中进行说明;比如,awakeAfterUsingCoder:来自于声明在单独头文件中的类别,不过它却在NSObject文档中进行了说明,因为它是个类方法,也是个全局方法,你可以随时使用它。其他一些则是使用受限的委托方法(其实是非正式协议),不需要集中说明;比如,animationDidStart:记录在CAAnimation类中,因为只有在使用CAAnimation时你才需要了解它。不过,每个对象都可以响应awakeFromNib,它对于你所编写的每个应用都是至关重要的,因此需要单独学习;对其的介绍在NSObject UIKit Additions Reference中,你可能找不到!同样,所有的键值编码方法(参见本章前面的介绍)与键值观测方法(参见第11章)都如此。
使出浑身解数找出了所有的NSObject方法后,你会发现它们可以很自然地划分为几类:
创建、销毁与内存管理
用于创建实例的方法,如alloc与copy,以及用于了解对象生命周期中发生了哪些事情的方法,如initialize与dealloc,还有用于管理内存的方法。
类关系
用于获取对象所属类与继承关系的方法,如superclass、isKindOfClass:与isMemberOfClass:。
对象内省与比较
用于确定假如向某个对象发送某条消息时将会发生什么事情的方法,如respondsToSelector:;用于将对象表示为字符串(description)与对象比较(isEqual:)的方法。
消息响应
用于确定当向某个对象发送某条消息时将会发生什么的方法,如doesNotRecognize-Selector:。如果感兴趣,请参见Objective-C Runtime Programming Guide。
消息发送
用于动态发送消息的方法。比如,performSelector:接收一个选择器作为参数,并会将其发送给一个对象,告诉该对象执行这个选择器。这看起来与将该消息发送给这个对象是一样的,不过如果直到运行期才知道所发送的消息该怎么做呢?此外,performSelector:的各变种可以在指定的线程上发送消息,或是经过一段时间后再发送消息(performSelector:withObject:afterDelay:等)。
performSelector...方法是Swift 2.0新引入的。之前,在Swift中是无法调用它的;我经常在Objective-C中使用它们,不过将代码转换为Swift迫使我寻找解决问题的其他方法,我发现在没有它们的情况下我也能很好地进行管理。