设为首页 加入收藏

TOP

【Swift学习】Swift编程之旅---构造方法(十八)(四)
2017-10-13 10:29:18 】 浏览:6513
Tags:Swift 学习 编程 之旅 --- 构造 方法 十八
中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。
 
Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能顺利完成:
 
  安全检查 1
指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。
 
如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。
 
  安全检查 2
指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。
 
  安全检查 3
便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。
 
  安全检查 4
构造器在第一阶段构造完成之前,不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self的值。
 
以下是两段式构造过程中基于上述安全检查的构造流程展示:
 
  阶段 1
某个指定构造器或便利构造器被调用;
完成新实例内存的分配,但此时内存还没有被初始化;
指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化;
指定构造器将调用父类的构造器,完成父类属性的初始化;
这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部;
当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段1完成。
 
  阶段 2
从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。
 
下图展示了在假定的子类和父类之间构造的阶段1: · 
 
在这个例子中,构造过程从对子类中一个便利构造器的调用开始。这个便利构造器此时没法修改任何属性,它把构造任务代理给同一类中的指定构造器。
 
如安全检查1所示,指定构造器将确保所有子类的属性都有值。然后它将调用父类的指定构造器,并沿着造器链一直往上完成父类的构建过程。
 
父类中的指定构造器确保所有父类的属性都有值。由于没有更多的父类需要构建,也就无需继续向上做构建代理。
 
一旦父类中所有属性都有了初始值,实例的内存被认为是完全初始化,而阶段1也已完成。
 
以下展示了相同构造过程的阶段2:
父类中的指定构造器现在有机会进一步来定制实例(尽管它没有这种必要)。
 
一旦父类中的指定构造器完成调用,子类的构指定构造器可以执行更多的定制操作(同样,它也没有这种必要)。
 
最终,一旦子类的指定构造器完成调用,最开始被调用的便利构造器可以执行更多的定制操作。
 
  构造器的继承和重载
跟 Objective-C 中的子类不同,Swift 中的子类不会默认继承父类的构造器。Swift 的这种机制可以防止一个父类的简单构造器被一个更专业的子类继承,并被错误的用来创建子类的实例。
 
假如你希望自定义的子类中能实现一个或多个跟父类相同的构造器--也许是为了完成一些定制的构造过程--你可以在你定制的子类中提供和重载与父类相同的构造器。
 
如果你重载的构造器是一个指定构造器,你可以在子类里重载它的实现,并在自定义版本的构造器中调用父类版本的构造器。
 
如果你重载的构造器是一个便利构造器,你的重载过程必须通过调用同一类中提供的其它指定构造器来实现。
 
注意:与方法、属性和下标不同,在重载构造器时你没有必要使用关键字override。
 
  自动构造器的继承
如上所述,子类不会默认继承父类的构造器。但是如果特定条件可以满足,父类构造器是可以被自动继承的。在实践中,这意味着对于许多常见场景你不必重载父类的构造器,并且在尽可能安全的情况下以最小的代价来继承父类的构造器。
 
假设要为子类中引入的任意新属性提供默认值,请遵守以下2个规则:
 
  规则 1
如果子类没有定义任何指定构造器,它将自动继承所有父类的指定构造器。
 
  规则 2
如果子类提供了所有父类指定构造器的实现--不管是通过规则1继承过来的,还是通过自定义实现的--它将自动继承所有父类的便利构造器。
 
即使你在子类中添加了更多的便利构造器,这两条规则仍然适用。
 
注意:子类可以通过部分满足规则2的方式,使用子类便利构造器来实现父类的指定构造器。
 
  指定构造器和便利构造器实战
接下来的例子将在实战中展示指定构造器、便利构造器和自动构造器的继承。它定义了包含三个类Food、RecipeIngredient以及ShoppingListItem的类层次结构,并将演示它们的构造器是如何相互作用的。
 
类层次中的基类是Food,它是一个简单的用来封装食物名字的类。Food类引入了一个叫做name的String类型属性,并且提供了两个构造器来创建Food实例:
class Food { var name: String init(name: String) { self.name = name } convenience init() { self.init(name: "[Unnamed]") } } 

 

下图中展示了Food的构造器链:
类没有提供一个默认的逐一成员构造器,所以Food类提供了一个接受单一参数name的指定构造器。这个构造器可以使用一个特定的名字来创建新的Food实例:
let namedMeat = Food(name: "Bacon") // namedMeat 的名字是 "Bacon” 

 

Food类中的构造器init(name: String)被定义为一个指定构造器,因为它能确保所有新Food实例的中存储型属性都被初始化。Food类没有父类,所以init(name: String)构造器不需要调用super.init()来完成构造。
 
Food类同样提供了一个没有参数的便利构造器 init()。这个init()构造器为新食物提供了一个默认的占位名字,通过代理调用同一类中定义的指定构造器in
首页 上一页 1 2 3 4 5 6 下一页 尾页 4/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇swift3.0的改变 下一篇FMDB基本使用

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目