设为首页 加入收藏

TOP

swift学习笔记3——类、结构体、枚举(二)
2017-10-10 12:15:31 】 浏览:1967
Tags:swift 学习 笔记 结构 枚举
总是向上代理,便利构造器必须总是横向代理

两段式构造

Swift 的两段式构造过程跟 Objective-C 中的构造过程类似。最主要的区别在于阶段 1,Objective-C 给每一个属性赋值0或空值(比如说0或nil)。Swift 的构造流程则更加灵活,它允许你设置定制的初始值,并自如应对某些属性不能以0或nil作为合法默认值的情况。

Swift 编译器将执行 4 种有效的安全检查,以确保两段式构造过程能不出错地完成:

安全检查 1

指定构造器必须保证它所在类引入的所有属性都必须先初始化完成,之后才能将其它构造任务向上代理给父类中的构造器。

如上所述,一个对象的内存只有在其所有存储型属性确定之后才能完全初始化。为了满足这一规则,指定构造器必须保证它所在类引入的属性在它往上代理之前先完成初始化。

安全检查 2

指定构造器必须先向上代理调用父类构造器,然后再为继承的属性设置新值。如果没这么做,指定构造器赋予的新值将被父类中的构造器所覆盖。

安全检查 3

便利构造器必须先代理调用同一类中的其它构造器,然后再为任意属性赋新值。如果没这么做,便利构造器赋予的新值将被同一类中其它指定构造器所覆盖。

安全检查 4

构造器在第一阶段构造完成之前,不能调用任何实例方法,不能读取任何实例属性的值,不能引用self作为一个值。

类实例在第一阶段结束以前并不是完全有效的。只有第一阶段完成后,该实例才会成为有效实例,才能访问属性和调用方法。

以下是两段式构造过程中基于上述安全检查的构造流程展示:

阶段 1

  • 某个指定构造器或便利构造器被调用。
  • 完成新实例内存的分配,但此时内存还没有被初始化。
  • 指定构造器确保其所在类引入的所有存储型属性都已赋初值。存储型属性所属的内存完成初始化。
  • 指定构造器将调用父类的构造器,完成父类属性的初始化。
  • 这个调用父类构造器的过程沿着构造器链一直往上执行,直到到达构造器链的最顶部。
  • 当到达了构造器链最顶部,且已确保所有实例包含的存储型属性都已经赋值,这个实例的内存被认为已经完全初始化。此时阶段 1 完成。

阶段 2

  • 从顶部构造器链一直往下,每个构造器链中类的指定构造器都有机会进一步定制实例。构造器此时可以访问self、修改它的属性并调用实例方法等等。
  • 最终,任意构造器链中的便利构造器可以有机会定制实例和使用self。
  1. 调用构造器后,先完成实例内存的分配,再保证自己引入的存储性属性都被初始化(有默认值也可),然后调用父类的构造器(没有设置默认值的存储性属性,必须在构造器初始化完成后再调用父类的指定构造器),当父类有多个指定构造器,只调用其中一个即可
  2. 存储性属性必须先初始化后才能使用,特别是在构造器中尤其要注意。父类的存储属性,只有调用完父类的构造器之后才能用self来右值访问,使用self来左值赋值
class Food {
    var name: String
    init(name: String) {
//        print(self.name)  // 这里访问会出错,因为self.name还没有初始化
        self.name = name    // 初始化 self.name
        print(self.name)    // 已经初始化,可以访问
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

可失败构造器

init?()

你可以用非可失败构造器重写可失败构造器,但反过来却不行。

当你用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。例如:

class UntitledDocument: Document {
    override init() {
        super.init(name: "[Untitled]")!
    }
}

类,结构体,枚举的可失败构造器可以横向代理到类型中的其他可失败构造器。类似的,子类的可失败构造器也能向上代理到父类的可失败构造器。

无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。

可失败构造器,可以不向上代理

必要构造器

在类的构造器前添加required修饰符表明所有该类的子类都必须实现该构造器:

class SomeClass {
    required init() {
        // 构造器的实现代码
    }
}

在子类重写父类的必要构造器时,必须在子类的构造器前也添加required修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加override修饰符:

class SomeSubclass: SomeClass {
    required init() {
        // 构造器的实现代码
    }
}

析构过程

析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。在类的定义中,每个类最多只能有一个析构器.

析构器是在实例释放发生前被自动调用。你不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用

因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为

枚举

Swift 中的枚举更加灵活,不必给每一个枚举成员提供一个值。如果给枚举成员提供一个值(称为“原始”值),则该值的类型可以是字符串,字符,或是一个整型值或浮点数。

下面是用枚举表示指南针四个方向的例子:

enum CompassPoint {
    case North
    case South
    case East
    case West
}

与 C 和 Objective-C 不同,Swift 的枚举成员在被创建时不会被赋予一个默认的整型值。在上面的CompassPoint例子中,North,South,East和West不会被隐式地赋值为0,1,2和3。相反,这些枚举成员本身就是完备的值,这些值的类型是已经明确定义好的CompassPoint类型。

原始值

enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

枚举类型ASCIIControlCharacter的原始值类型被定义为Character,对于一个特定的枚举成员,它的原始值始终不变

首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇swift学习笔记1——基础部分 下一篇[Swift]String的坑

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目