Objective-C 编程语言(2) 类,对象,消息 --- 类(1)继承

面向对象的程序通常都是有各种各样的对象构成的。基于Cocoa框架的程序可能用到了NSMatrix对象,NSWindow对象,NSDictionary对象,NSFont对象,NSText对象以及其他的许多种类的对象。程序中通常也是使用某个类的多个对象,而不是一个。比如,多个NSArray类的对象或者是多个NSWindow类的对象。

在Objective-C中,通过定义对象所属的类来定义对象。类是一种类型对象的原型。其中声明了属于该类的每个对象的成员变量,并定义了该类所有对象都能使用的方法。

编译器只会为一个类创建一个可以访问的对象,那就是类对象。类对象知道如何去生成该类的对象。正是基于这种原因,类对象通常都会被称为是工厂对象。类对象是类在编译时期的版本。类对象生成的对象才是类的实例。程序中进行实际工作的都是由该类对象创建出来的实例对象。

类的所有实例共有一套方法集,但是各自都含有自身的实例变量。每一个对象的实例变量是自己独有的,但是所有方法是共享的。

按照惯例,类名称首字母一般是大写的,例如Rectangle。实例变量的名称首字母通常是小写的,如myRectangle。

继承

类的定义是具有追加性的。每一个从其他类派生而来的新类都继承了其中的方法和实例变量。新的类知识简单地对这些继承的东西进行追加或者修改,而不需要复制其对应的代码。

继承把所有的类都关联起来,从而形成一个具有唯一一个根节点的继承关系图。当编写基于“Foundation framework基础框架”的程序的时候,这个跟类就是NSObject。继承图中出了跟类之外的所有类都有一个超类,这个超类在继承关系图上距离跟类更进一步。任何类,包括跟类都可以是多个类的超类,其子类在继承关系图上距离跟类更远一步。图1-1展示了在绘图程序中可能用到得类的继承关系图:

《Objective-C 编程语言(2) 类,对象,消息 --- 类(1)继承》

从上图中可以看出Square类是Rectangle类的子类;Rectangle类Shape类的子类;Shape类是Graphic类的子类;Graphic类是NSObject类的子类。 继承关系具有累积性。因此,Square类的对象拥有在Rectangle类,Shape类,Graphic类已经NSObject类中定义的方法以及实例变量。当然Square类的对象拥有在Sqaure类自身中定义的实例变量和方法。简而言之,一个Square类的对象不仅仅是一个方形(square),同时也是一个矩形(rectangle),也是一个形状(shape),也是一个图形(Graphic),还是一个对象(NSObject)。

除了NSObject类之外的每一个类都可以被看做是对其超类的具体化。其后的每一个子类都是对这种累积继承的修改。Square类中定义的只是把一个矩形rectangle变成是一个方形所需的最小修改。

在定义类的时候是通过声明其超类来把该类连接到类的继承关系图中的。我们创建的每一个类都应该是另外一个类的子类,除非我们想要定义的是跟类。其中可选的超类是很多的。Cocoa中包含了NSObject以及几个框架。其中就包含了超过250多种的类。其中有很多类是我们可以直接拿来在我们的程序中使用的。还有一些其他的类,我们可以根据我们的需要,通过定义其子类来对其进行修改。

上面说到的一些框架类几乎定义了我们所需的全部东西,但是把某些详细的实现留个了子类。因此我们只需要根据我们的需要编写少量的代码,而复用这些已有的东西来快速地编写出可靠的代码。

NSObject类

NSObject类是一个跟类,他不需要超类。其中定义了Objective-C中的对象以及对象交互所需的基本框架。通过继承NSObject类,这种对象的行为能力以及与运行时交互的能力在其子类中传播

开来。

不需要从别的类中继承特殊行为的类至少应该是NSObject类的子类。因为该类的实例至少在运行时应该具有Objective-C对象所具有的基本能力。从NSObject类继承这种能力和在新类中重新定义这些能力先比则更简单并且更可靠。

注意:实现一个全新的跟类是一件十分复杂的任务,并且具有潜在的危险性。该类必须重复在NSObject类中已经完成的大量工作。比如分配实例,并把它与对应的类相关联,并在运行时对其进行鉴别等。正是如此,我们应该使用Cocoa中提供的NSObject类来作为跟类。更多信息参见《NSObject 协议参考》一书中的“NSObject 类参考”章节。

继承实例变量

当一个类对象创建一个新的实例的时候,该实例不仅含有其类中所定义的实例变量,还含有其类的超类中所定义的实例变量,还含有其超类的超类中所定义的实例变量,这样依此类推知道根类。这样以来NSObject类中定义的实例变量就成了所有对象都拥有的变量。正是isa 把一个对象和其对应的类联系起来了。

图1-2展示了一种可能的Rectangle类的实现,其中包含包含了Rectangle类中定义的变量,Shape类中定义的变量,Graphic类中定义的变量以及NSObject类中定义的变量。

《Objective-C 编程语言(2) 类,对象,消息 --- 类(1)继承》

类中定义实例变量并不是必要的。一个类可以只定义新的方法,而完全依靠继承而来的那些实例变量。例如,Square类自身中就可以不用定义任何实例变量。

继承方法

对象不仅可以访问其类中定义的方法,还可以访问其超类中定义的方法,以及其超类的超类中定义的方法,依此类推知道根类。例如,Square类的对象就可以使用Rectangle类,Shape类,Graphic类以及NSObject类中定义的方法。

因此,程序中的新定义的类可以复用在继承关系图图中位于其上方的所有超类的代码。这种继承机制是面型对象编程的一个很重要的好处。当我们Cocoa提供的面向对象的框架类的时候,我们的程序就可以利用这些已经为该框架类编写好的代码功能。我们在程序中只需要根据需要增加一些代码来完成特性的功能。

在继承关系图中,类对象也继承了位于其上方的超类。但是由于类对象没有实例变量,因此他们只是继承了方法。

方法的覆盖

继承机制中有一个很有用的特殊情况:在定义新的类的时候,我们可以在新的类中实现在继承关系图途中和其超类中同名的函数。这个新的方法就覆盖了原来的方法。新的类的实例将执行的是该新的方法而不是原来的方法。该新类的派生类继承的也将是该新的方法而不是原来的方法。

例如,Graphic类中定义了display方法,而在在Rectangle类中是可以通过定义新的display的实现而覆盖继承而来的原有的display方法。Graphic类中的方法对于它的所有派生类都是可用的,而Rectangle类除外,Rectangle类将执行的是自身的Rectangle类的display方法。

尽管这种覆盖机制可以阻止原来的方法被继承,但是新类中的其他方法却是可以跳过这个新的方法而使用原来的方法的。更多信息参见“向self和super发送消息”章节。

新定义的方法中是可以使用被他覆盖的原来的方法的。此时,新的方法是对原有方法的更新或者是修改,而不是对其进行完全的替换。当继承关系图中的多个类都定义了同样的方法,但是如果每一个的实现都使用到了被覆盖的原来的方法的话,原来方法中的实现便会在这些类中蔓延开来。

尽管派生类是可以覆盖继承而来的方法的,但是派生类不能覆盖继承而来的实例变量。这是因为在为对象分配空间的时候,是为对象的每个实例变量,包括继承而来的实例变量都要分配空间。因此不能企图通过在派生类中定义与超类中同名的实例变量来覆盖超类的实例变量。如果确实遇到了这样的代码,编译时,编译器会报告告警或者错误的。

抽象类

一些类在设计的时候就是希望被别的类继承。这种抽象的类只是方法和实例变量组合起来,以便于可以被更加通用的派生类使用。这种抽象类自身是不完全的,但是含有能够减轻其派生类代码负担的有用代码。由于抽象类必须有派生类才显得有意义,因此它们有时会被成为抽象的超类。

与其他的语言不同的是Objective-C语言中没有专门的语法来标识一个类是抽象类,因此也不会阻止我们在程序中创建抽象类的实例。

在Cocoa中,NSObject类就是公认的这样的抽象类。在程序中我们是不会直接使用到NSObject类的实例的。因为这样做没有任何用,这样的对象将是一个泛化的对象,他没有任何具体的功能    。

NSView类也是抽象类的一个例子。该类的实例可能会被偶尔使用到。

抽象类中通常含有的是用于定义程序结构的代码。当我们定义这些抽象类的派生类的以后,这些派生类可以很方便地适应程序框架工作,并自动地相互配合工作。

下图为今年部分iOS开发的视频教程,因为不定时更新中故不做多的截图,如果有iOS开发上的问题不懂或者需要视频教程可以看我的个人简介。

因为三月还没结束,故不截图,不定时更新中。

《Objective-C 编程语言(2) 类,对象,消息 --- 类(1)继承》

    原文作者:iOS开发攻城狮
    原文地址: https://www.jianshu.com/p/1c85b64fc49b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞