关于继承中的构造规则是一个难点。

假设有问题,请留言问我。

我的Swift新手教程专栏

http://blog.csdn.net/column/details/swfitexperience.html

为什么要有构造器:为类中自身和继承来的存储属性赋初值。

一、两种构造器-指定构造器和便利构造器

指定构造器:类中必备的构造器。为全部的属性赋初值。(有些子类可能不须要显示声明,由于默认从基类继承了)

便利构造器:类中的辅助构造器,通过调用指定构造器为属性赋初值。(仅在必要的时候声明)

举例

class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}

便利构造器通过conveniencekeyword声明,能够看到。便利构造器是通过调用指定构造器来进行构造的。这也就是一个关键的概念:横向代理。

何为代理:就是让别人帮你干活

二、构造过程中的规则

(一)构造器链就是调用构造器的顺序

规则例如以下:

1.1、指定构造器必须调用其父类的指定构造器

1.2、便利构造器必须调用同一类中的指定构造器

1.3、便利构造器必须最后以调用一个指定构造器而结束

总得来说一句话:便利构造器横向代理,指定构造器向上代理。

举个样例:

class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
convenience init(){
self.init(baseInput:"")
}
}
class Sub:Base{
var subVar:String;
init(subInput:String,baseInput:String){
subVar = subInput
super.init(baseInput:baseInput)//这里是规则1.1
}
convenience init(conSubInput:String){
self.init(subInput:conSubInput,baseInput:"")//这里是规则1.2
}
convenience init(){
self.init(conSubInput:"")//这里是规则1.3,由于调用了另外一个便利构造器,而另外一个便利构造器以调用指定构造器结束
}
}

(二)关于构造器的继承与重载

swift中,子类不会默认继承父类的构造器。

构造器的重载遵循构造器链的规则(1.1-1.3)

构造器的继承规则例如以下:

2.1、假设子类中未定义不论什么指定构造器,将会自己主动继承全部父类的指定构造器

2.2、假设子类中提供了全部父类指定构造器,无论是通过规则2.1继承来的,还是自己定义实现的,它将继承全部父类的便利构造器。

注意:子类能够通过部分满足规则2.2的方式,使用子类便利构造器来实现父类的指定构造器。

样例一:

class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
convenience init(){
self.init(baseInput:"basevar")
}
}
class Sub:Base{
var subVar:String = "subvar";
}

这里子类未定义不论什么构造器,所以满足规则2.1,2.1,将继承全部父类的指定构造器和便利构造器

所以能够这么调用

var instance1 = Sub()
var instance2 = Sub(baseInput:"newBaseVar")

样例二

class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
init(firstPart:String,secondPart:String){
baseVar = firstPart + secondPart
}
convenience init(){
self.init(baseInput:"basevar")
}
}
class Sub:Base{
var subVar:String;
init(subInput:String,baseInput:String){
subVar = subInput
super.init(baseInput)
}
}

这里,子类仅仅是实现了父类的一个构造器,所以并未继承便利构造器,也没有继承另外一个指定构造器

仅仅能够这么创造实例

var instance = Sub(subInput:"subvar",baseInput:"basevar")

(三)基于上述两个规则,构造过程分为两个部分

阶段一

  • 某个指定的构造器或者便利构造器被调用;
  • 完毕新实例的内存分配(此时内存尚未初始化)。
  • 指定构造器确保其引入的全部存储属性已经赋值(存储属性极其所属内存完毕初始化)。
  • 指定构造器调用父类构造器(父类构造器属性初始化)。
  • 这个调用父类的构造器沿着构造器链一直向上。直到最顶部。(确保全部的继承的基类过程都已经初始化)。

阶段二

  • 从顶部一直向下。每一个构造器链中类指定的构造器都有机会进一步定制实例。构造器此时能够訪问self,改动它的属性而且调用实例方法等等。
  • 终于,随意构造器的便利构造器将有机会定制实例和使用self。

可能这个规则有点抽象。举个样例就明确了

class Base{
var baseVar:String
init(baseInput:String){
baseVar = baseInput
}
}
class Sub:Base{
var subVar:String;
func subPrint(){
println("如今能够调用实例方法了")
}
init(subInput:String,baseInput:String){
subVar = subInput
super.init(baseInput:baseInput)
//这里就完毕了阶段一
self.subVar = subInput + "123"//此时能够调用self
subPrint()//此时也能够调用实例方法了
}
}

总得来说:当类的实例的内存被初始化完毕,也就是调用super.init()之后,就完毕了阶段一了。



三、编译器的安全检查

检查一


  指定构造器必须在它所在类的属性先初始化完毕后才干把构造任务向上代理给父类中的构造器。简单来说。就是先初始化自己的存储属性,在调用父类的super.init向上初始化

检查二

  指定构造器必须先向上调用父类构造器。在为继承来的属性赋初值。

这个非常简答,如果继承来个x,你先为x赋值为1了,而在调用父类构造器。父类构造器会为x赋另外一个初值来保证初始化过程完毕,那么你赋值的1就被覆盖了

检查三

  便利构造器先调用同类中其它构造器,再为随意属性赋初值。和检查二类似。也是防止被覆盖

检查四

  构造器在第一阶段完毕之前。不能饮用self,不能调用不论什么实例属性,不能调用实例方法





四、总结一下

指定构造器的过程是这种

1、为自己的属性赋初值

2、调用基类构造器(super.init)

3、然后就能够调用self,和实例方法,存储属性。定制新的值了。

然后,我们看下官方文档里给出的一个比較好的样例

class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name.lowercaseString)" {
output += purchased ? " YES" : " NO"
return output
}
}

这个构造器链的关系如图



解释

  • 基类Food定义了一个指定构造函数和一个便利构造器
  • 子类RecipeIngredient实现了基类Food全部的指定构造器。所以它继承了基类的便利构造器
  • 子类ShoppingListItem未定义构造器,所以继承了基类RecipeIngredient的全部构造器。Swift入门系列15-继承中的构造规则(难点)

Swift难点-继承中的构造规则实例具体解释的更多相关文章

  1. C++ 类的继承三(继承中的构造与析构)

    //继承中的构造与析构 #include<iostream> using namespace std; /* 继承中的构造析构调用原则 1.子类对象在创建时会首先调用父类的构造函数 2.父 ...

  2. C++学习笔记-继承中的构造与析构

    C++存在构造函数与析构函数,继承中也存在构造和析构函数.继承中的构造和析构函数与普通的构造析构有细微差别. 赋值兼容性原则 #include "iostream" using n ...

  3. C++继承中的构造和析构

    1,构造:对象在创建的后所要做的一系列初始化的工作: 析构:对象在摧毁之前所要做的一系列清理工作: 2,思考: 1,子类中如何初始化父类成员? 1,对于继承而言,子类可以获得父类的代码,可以获得父类中 ...

  4. C++语法小记---继承中的构造和析构顺序

    继承中构造和析构的顺序 先父母,后客人,最后自己 静态变量和全局变量在最开始 析构和构造的顺序完全相反 #include <iostream> #include <string> ...

  5. 【Java学习笔记之二十二】解析接口在Java继承中的用法及实例分析

    一.定义 Java接口(Interface),是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为( ...

  6. C++ //继承中构造和析构顺序

    1 #include <iostream> 2 #include <string> 3 using namespace std; 4 5 class Base 6 { 7 pu ...

  7. C++类继承中,基类/当前对象属性/当前对象的构造顺序

    [1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...

  8. 前端知识体系:JavaScript基础-原型和原型链-理解 es6 中class构造以及继承的底层实现原理

    理解 es6 中class构造以及继承的底层实现原理 原文链接:https://blog.csdn.net/qq_34149805/article/details/86105123 1.ES6 cla ...

  9. Android(java)学习笔记119:继承中父类没有无参构造

    /* 如果父类没有无参构造方法,那么子类的构造方法会出现什么现象呢? 报错. 如何解决呢? A:在父类中加一个无参构造方法 B:通过使用super关键字去显示的调用父类的带参构造方法 C:子类通过th ...

随机推荐

  1. win32 字体变换与窗口同大同小

    #include <windows.h> #include "res/resource.h" LRESULT CALLBACK WinProc(HWND hwnd, U ...

  2. 查找MobileSafari WebKit revision number的方法

    Mobile Safari是开源的Mac Safari的iOS版本,然而iOS WebKit并不完全开源,只公开了部分的WebCore和JavaScriptCore.有时需要知道iOS Safari的 ...

  3. Atitit.jquery 版本号新特性attilax总结

    Atitit.jquery 版本号新特性attilax总结 1. Jq1.4 1 2. 1.5 1 3. 1.6 3 4. Jq1.7 3 ⒉提升了事件委派时的性能有了大幅度的提升.尤其是在ie7下: ...

  4. Cocos2dx-Android 之Makefile通用高级写法

    不废话,直接贴.节约时间.须要改进的地方请各位指出.共同学习. LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := c ...

  5. Swift - 使用UIScrollView实现页面滚动切换

    UIScrollView提供了以页面为单位滚动显示各个子页面内容的功能,每次手指滑动后会滚动一屏的内容.   要实现该功能,需要如下操作: 1,将UIScrollView的pagingEnabled属 ...

  6. xcode5下一个ffmpeg静态库配置

    1.若要安装xcode命令行工具 1).xcode5安装命令行工具方法: 在终端运行命令Using xcode-select --install 2).xcode5之前安装命令行工具方法: 2.xco ...

  7. cURL安装和使用笔记

    0.前言     cURL是一个利用URL语法在命令行下工作的文件传输工具.它支持文件上传和下载,所以是综合传输工具,但习惯称cURL为下载工具.cURL还包含了用于程序开发的libcurl.cURL ...

  8. 利用手工编码的方式对srtus2进行输入验证

    对action方法进行校验有两种方法一种是: 1手工编码书写 2一种是用xml 输入校验的流程: 1类型转化器对请求参数执行类型转化,并把转换后的值赋给action属性. 2.如果执行类型转化的过程中 ...

  9. 网站集成QQ登录功能(转)

    最近在做一个项目时,客户要求网站能够集成QQ登录的功能,以前没做过这方面的开发,于是去QQ的开放平台官网研究了一下相关资料,经过自己的艰苦探索,终于实现了集成QQ登录的功能,现在把相关的开发经验总结一 ...

  10. 谈谈android反编译和防止反编译的方法(转)

    谈谈android反编译和防止反编译的方法(转) android基于java的,而java反编译工具很强悍,所以对正常apk应用程序基本上可以做到100%反编译还原. 因此开发人员如果不准备开源自己的 ...