iOS 根类NSObject

晋涛
2023-12-01

根类不从其它类继承,但是类层次中的所有其它类都最终从根类继承下来。根类连同Objective-C语言,是Cocoa直接访问Objective-C运行环境或与之交互的基本途径。

Cocoa对象 根类是本文要介绍的内容,仅凭Objective-C语言和运行环境并不足以构造哪怕是最简单的面向对象的程序,至少是不容易的。还缺少一些东西:即所有对象公有的基本行为和接口的定义。根类正是提供了这些定义。

之所以叫根类,是因为它位于整个类层次(这里是指Cocoa的类层次)的根上。根类不从其它类继承,但是类层次中的所有其它类都最终从根继承下来。根类连同Objective-C语言,是Cocoa直接访问Objective-C运行环境或与之交互的基本途径。Cocoa对象的大部分对象行为能力都是从根类得到的。

Cocoa提供了两个根类:NSObject和NSProxyCocoa将后者定义为抽象类,用于表示其它对象的替身对象。因此NSProxy类在分布式对象架构中是很重要的。由于作用比较特别,NSProxy在Cocoa程序中出现频率很低。Cocoa开发者在提到根类时,几乎总是指NSObject。

本部分将讨论NSObject类,看看它如何与运行环境进行交互,以及它为所有Cocoa对象定义的基本行为和接口。其中主要是它为对象的内存分配、初始化、内存管理、内省、以及运行环境支持所声明的方法。这些概念是理解Cocoa的基础。

NSObject

NSObject是大多数Objective-C类层次的根类,它没有超类。其它类从NSObject继承访问Objective-C语言运行时系统的基本接口,它们的实例可以得到对象行为的能力。

虽然NSObject不是一个严格的抽象类,但它是个虚类。仅凭一个NSObject实例除了作为一个简单的对象外,不能完成任何有用的工作。为了在您的程序中加入特有的属性和逻辑,必须创建一个或多个从NSObject或其派生类继承下来的类。

NSObject采纳了NSObject协议(参见"根类—和协议"部分)。NSObject协议支持多个根对象。举例来说,NSProxy是另一个根类,它不是继承自NSObject,但采纳了NSObject协议,以便和其它Objective-C对象共用一个公共的接口

NSObject是Cocoa中所有类的根类,包括Foundation和Application Kit。

根类—和协议

NSObject不仅仅是一个类的名称,还是一个协议的名称。两者对于定义一个Cocoa对象都是必要的。NSObject协议指定了Cocoa中所有根类必须的基本编程接口,因此不仅NSObject类采纳了这个同名的协议,其它根类也采纳这个协议,比如NSProxy。NSObject类进一步指定了不作为代理对象的Cocoa对象的基本编程接口。

NSObject及类似的协议用于Cocoa对象的总体定义(而不是在类接口中包含那些协议),使多个根类成为可能。每个根类共用一个由它们采纳的协议定义的公共接口。

在另一种意义上,NSObject不仅仅是个“根”协议。虽然NSObject类没有正式采纳NSCopying、NSMutableCopying、和NSCoding协议,但它声明和实现了与那些协议相关的方法(而且,包含NSObject类的NSObject.h头文件中也包含上面提到的所有四个协议的定义)。对象拷贝、编码、和解码是对象行为的基本部分。很多子类(如果不是绝大多数的话)都希望采纳和遵循这些协议。

请注意:其它Cocoa类可以通过范畴将方法添加到NSObject中。这些范畴通常是一些非正式的协议,在委托中使用。它们允许委托对象选择实现范畴中的部分方法。然而,NSObject的范畴并不被认为是基本对象接口的一部分。

根类方法概述

NSObject根类和它采纳的NSObject协议及其它“根” 协议一起,为所有不作为代理对象的Cocoa对象指定了如下的接口和行为特征:

分配、初始化、和复制。NSObject类中的一些方法(包括一些来自协议的方法)用于对象的创建、初始化、和复制:

  • alloc和allocWithZone:方法用于从某内存区域中分配一个对象内存,并使对象指向其运行时的类定义。
  • init方法是对象初始化原型,负责将对象的实例变量设置为一个已知的初始状态。initialize和load是两个类方法,它们让对象有机会对自身进行初始化。
  • new是一个将简单的内存分配和初始化结合起来的便利方法。
  • copy和copyWithZone:方法用于拷贝实现这些(由NSCopying协议定义的)方法的类的实例。希望支持可变对象拷贝的类则需要实现mutableCopy和mutableCopyWithZone:(由NSMutableCopying协议定义)方法。

对象的保持和清理。下面的方法对面向对象程序的内存管理特别重要:

  • retain方法增加对象的保持次数。
  • release方法减少对象的保持次数。
  • autorelease方法也是减少对象的保持次数,但是以推迟的方式。
  • retainCount方法返回对当前的保持次数。
  • dealloc方法由需要释放对象的实例变量以及释放动态分配的内存的类实现。

更多信息请参见 Cocoa对象的生命周期

内省和比较。NSObjec有很多方法可以查询对象的运行时信息。这些内省方法有助于找出对象在类层次中的位置,确定对象是否实现特定的方法,以及测试对象是否遵循某种协议。这些方法中的一部分仅实现为类方法。

  • superclass和class方法(实现为类和实例方法)分别以Class对象的形式返回接收者的超类和类。
  • isKindOfClass:和isMemberOfClass:方法来确定对象属于哪个类。后者用于测试接收者是否为指定类的实例。
  • isSubclassOfClass:类方法则用于测试类的继承性。
  • respondsToSelector:方法用于测试接收者是否实现由选择器参数标识的方法。instancesRespondToSelector:类方法则用于测试给定类的实例是否实现指定的方法。
  • conformsToProtocol:方法用于测试接收者(对象或类)是否遵循给定的协议。
  • isEqual:和hash方法用于对象的比较。
  • description方法允许对象返回一个内容描述字符串;这个方法的输出经常用于调试(“print object”命令),以及在格式化字符串中和“%@”指示符一起表示对象。

更多信息请参见 内省 。

对象的编码和解码。下面的方法和对象的编解码(作为归档过程的一部分)有关:

  • encodeWithCoder:和initWithCoder:是NSCoding协议仅有的方法。前者使对象可以对其实例变量进行编码,后者则使对象可以根据解码过的实例变量对自身进行初始化。
  • NSObject类中声明了一些于对象编码有关的方法:classForCoder:、replacementObjectForCoder:、和awakeAfterUsingCoder:

消息的转发。forwardInvocation:和相关的方法允许一个对象将消息转发给另一个对象。

消息的派发。以performSelector...开头的一组方法使您可以在指定的延迟后派发消息,以及将消息从辅助线程派发(同步或异步)到主线程。

NSObject还有几个其它的方法,包括一些处理版本和姿态(后者使一个类在运行时将自己表示为另一个类)的类方法,以及一些访问运行时数据结构的方法,比如方法选择器和指向方法实现的函数指针。

接口规范

某些NSObject方法只是为了被调用,而另一些方法则是为了被重载。举例来说,大多数子类不应该重载allocWithZone:方法,但必须实现init方法—至少需要实现一个最终调用根类的init方法(请参见"对象的创建"部分)的初始化方法。对于那些期望子类重载的方法,NSObject的实现或者什么也不做,或者返回一个合理的值,比如self。这些缺省实现使我们有可能向任意的Cocoa对象—甚至是没有重载这些方法的对象—发送诸如init这样得基本消息,而又不必冒运行时例外的风险。在发送消息之前,不必进行检查(通过respondsToSelector:方法)。更加重要的是,NSObject的这些“占位”方法为Cocoa对象定义了一个公共的结构,并建立了一些规则,如果所有的对象都遵循这些规则,对象间的交互将更加可靠。

实例方法和类方法

运行环境系统以一种特殊的方式处理根类定义的方法。根类定义的实例方法可以由实例对象和类对象执行,因此所有类对象都可以访问根类定义的实例方法。对于任何类对象,如果对象中不包含同名的类方法,就可以执行根类的所有实例方法。

举例来说,一个类对象可以通过发送消息来执行NSObject的respondsToSelector:和performSelector:withObject:实例方法:

  1. SEL method = @selector(riskAll:);   
  2. if ([MyClass respondsToSelector:method])   
  3.     [MyClass performSelector:method withObject:self];  

请注意,只有根类中定义的实例方法才可以在类对象中使用。在上面的例子中,如果MyClass重新实现了respondsToSelector:或者performSelector:withObject:方法,则那些新的版本将只能用于实例对象。MyClass的类对象只能执行NSObject类定义的版本(当然,如果MyClass将respondsToSelector:或performSelector:withObject: 实现为类方法,而不是实例方法,则该类对象可以执行这些新的实现)。


 类似资料: