7.5.3 类的初始化
类的初始化通用语法看起来跟值类型差不多,不过有必要注意一些不同的规则。存在这些额外的规则主要是因为类可以继承,而这必然会增加初始化的复杂度,特别是类增加了指定(designated)初始化方法和便捷(convenience)初始化方法的概念,类的初始化方法一定是二者之一。指定初始化方法负责确保初始化完成前所有的属性都有值,以便实例可用;而便捷初始化方法是指定初始化方法的补充,通过调用所在类的指定初始化方法来实现,主要作用通常是为某种特殊目的创建实例。在本小节的内容中,将会介绍委托初始化方法的使用。
现在先看一下类的默认初始化方法,如果所有的属性都有默认值并且没有自定义初始化方法,类会得到一个默认的空初始化方法。与结构体不同,类没有默认的成员初始化方法,这就解释了为什么之前要给类的属性设置默认值:因为这样就可以利用编译器提供的空初始化方法。
这里要着重看一下类被继承之后的初始化过程:
动手写7.5.4 InitPractise->SuperCar.swift
本例中,定义了一个SuperCar类,继承自Car类,并且增加了属性maxSpeed。此外,实现了一个简单的初始化方法:传入参数maxSpeed用于给属性maxSpeed赋值,接着再调用父类中的初始化方法(注意:必须先赋值后再调用父类初始化方法,否则编译器会报错)。回到main.swift中,创建一个SuperCar的实例:
动手写7.5.5 InitPractise->main.swift
控制台输出如下:
有时候类中的属性值很多,如果想要简单地初始化一个类实例需要编写很长的代码,这时候就可以使用委托初始化。所谓委托初始化就是指初始化方法的定义中可以包含对该类型其他初始化方法的调用,这个过程被称为委托初始化(initializer delegation),通常用来提供多种创建实例的路径。对于值类型(也就是枚举和结构体)来说,委托初始化相对比较直观,因为值类型不能继承,所以值类型的委托初始化只涉及调用当前类型的其他初始化方法;对于类来说,委托初始化则更复杂。现在我们来看看Car提供的两个初始化方法,它们实现得有些烦琐,所以可以使用委托初始化的方式将代码精简,代码如下:
动手写7.5.6 InitPractise->Car.swift
之前提供的初始化方法都是一一给属性赋值,现在提供一个涵盖所有属性的初始化方法,并且将缺少参数的init方法定义为convenience,在调用时最终还是调用了最初那个init方法,只是将缺少的值用参数的方式传递下去。委托初始化可以避免代码重复,避免代码重复不仅能节省输入量,还可以减少bug。开发者不需要重新输入把初始化方法的参数值传给类型属性的代码,只要调用另一个初始化方法就可以了。当两个地方有相同代码的时候,只要有改动就必须记着同时修改两处。可以说委托初始化为创建实例“定义了路径”,一个初始化方法调用另一个初始化方法,帮助实例给所有需要赋值的属性赋值,最终委托初始化会到达一个初始化方法,当这个初始化方法调用完成,这个实例便已经是完整的了。
因为有了自定义的初始化方法,编译器就不再提供默认的成员初始化方法。举个例子,如果开发者要创建的车子没有颜色信息,那就可以用这个新的初始化方法。在本例中,可以用方便的新初始化方法通过color或者brand为对应的属性赋值,与此同时,保证缺失的属性也能有默认值。