初始化的过程包括为每一个存储属性设置一个初始值和其他步骤。通过定义构造函数来实现初始化的过程,跟oc的初始化函数不同,Swift的构造函数不返回一个值。它们的主要角色是确保一个类型的实例在初次使用前被正确的初始化。

类的实例也可以有析构函数,析构函数在类的实例在释放前完成一些清理工作。

Setting Initial Values for Stored Properties

类和结构体必须为它们所有的存储属性设置一个初始值,在类或结构体的实例创建完成前。存储属性不能是不确定的状态。

可以通过构造函数或默认值的方式给存储属性一个初始值,而且通过这两种方式,属性的值都是被直接设置,不会调用属性观察者。

Initializers(构造函数)

init() {

}

Customizing Initialization(自定义初始化函数)

你可以自定义初始化过程,通过使用输入参数,可选的属性类型,或者在初始化期间给常量属性赋值。具体描述在下面的部分中描述。

1. Initialization Parameters

你可以为构造函数提供参数,看个例子:

struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
} let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.5) let bodyTemperature = Celsius(37.0)

Parameter Names and Argument Labels

Swift provides an autoamtic argument label for every parameter in an initializer if you don't provide one.看个例子:

struct Color {
var red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}

我们可以这样初始化一个Color的实例:

let megenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)

Initializer Parameters Without Argument Labels

参考上边Celsius 类中的第三个构造方法,及bodyTemperature 的初始化。

2. Optional Property Types

如果你自定义的类型有一个存储属性在逻辑上允许“没有值”——或许是因为在初始化期间不能设置设置它的值,也可能是在后边的某个时间点允许它“没有值”,那么可以把这个属性声明为optional type。optional type的属性被自动初始化为nil。原文如下:

“If your custom type has a stored property that is logically allowed to have “no value”—perhaps because its value cannot be set during initialization, or because it is allowed to have “no value” at some later point—declare the property with an optional type. Properties of optional type are automatically initialized with a value of nil, indicating that the property is deliberately intended to have “no value yet” during initialization.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3).” iBooks.

看下下边这个例子:

class SurveyQuestion {
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
} let cheeseQuestion = SurveyQuestion(text: "Do you like cheese")
cheeseQuestion.ask()
cheeseQuestion.response = "YES, i do like chesse."

3. Assigning Constant Properties During Initialization

你可以在初始化期间给一个常量属性赋一个值,只要在初始化完成时有一个明确的值就可以。

注意:对于类的实例,常量属性只能由引入该属性的类在初始化期间修改,不能被子类修改。

可以在上边那个类稍作修改,观察text属性:

class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
} let cheeseQuestion = SurveyQuestion(text: "Do you like cheese")
cheeseQuestion.ask()
cheeseQuestion.response = "YES, i do like chesse."

Default Initializers(默认构造函数)

在类或结构体为每个属性提供默认值并且没有其他构造函数时,Swift提供默认构造函数。默认构造函数简单的创建一个实例并把其所有属性设置成默认值。

class ShoppingList {
var name: String?
var quantity = 1
var purchased = false
} var item = ShoppingList()

上面的例子中,如果我们不给quantity设置默认值,编译器将提示ShoppingList类没有构造函数。

Memberwise Initializers for Structure Types

结构体类型如果没有自定义的构造函数,那么会有一个memberwise initializer

struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

Initializer Delegation for Value Types

Initializers can call other initializers to perform part of an instance's initialization. This process, known as initializer delegation, avoids duplicating code across multiple initializers.

对于值类型,可以使用self.init来引用该类型的其他构造函数。self.init只能在构造函数内使用。看个例子:

struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var size = Size()
var point = Point()
init() {}
init(size: Size, point: Point) {
self.size = size
self.point = point
}
init(size: Size, center: Point) {
let originX = center.x - size.width / 2
let originY = center.y - size.height / 2
self.init(size: size, point: Point(x: originX, y: originY))
}
}

上面例子中第一个构造函数其实就相当于默认构造函数,第二个相当于memberwise initializer,第三个是完全自定义的。这个例子还有一种写法,可以不写init()init(size:point:),需要用到extension。

如果为值类型自定义了一个构造函数,那就不能再访问默认构造函数了。如果你想要你定义的值类型既可以使用默认构造函数,也可以使用自定义的构造函数,那需要把自定义的构造函数写在extension中。

Class Inheritance and Initialization

一个类所有的存储属性包括从父类继承的属性,都以徐在初始化几千有一个初始值。

Swift为类类型定义了两种类型的构造函数来确保存储属性有一个初始值,分别是designated initializerconvenience initializer。在下面的讨论中,我们分别把这两种称为指定构造函数、便利构造函数。

每个类都必须至少有一个指定构造函数。在一些条件下,这个需求通过从父类继承指定构造函数来满足。

便利构造函数可以调用该类中的指定构造函数。

Syntax for Designated and Convenience Initializers

指定构造函数的语法:

便利构造函数的语法:

Initializer Delegation for Class Types

为了简化指定构造函数与遍历构造函数之间的关系,Swift在构造函数间的delegation call上使用下边三条规则:

  1. A designated initializer must call a designated initializer from its immediate superclass.
  2. A convenience initializer must call another initializer from the same class.
  3. A convenience initiazlier must ultimately call a designated initializer.

可以简单的这样记:

  • Designated initializers must always delegate up.
  • Convenience initiazlizers must always delegate across.

这里有一个示意图来说明上面描述的规则:

Two-Phase Initialization

在Swift中类的初始化分为两个阶段。在第一个阶段,类的每一个存储属性由引入其的类赋一个初始值,一旦每一个存储属性的初始状态确定了,第二个几段就开始了。第二个阶段开始以后,每个类在类可以使用前都有机会给每一个存储属性进行进一步自定义。

跟oc中的初始化有些许不同,主要体现在第一个阶段,在第一个阶段中,oc给每一个属性赋0值(如0、nil)。

Swift的编译器执行四步安全性检查(safaty-check)来确保两个阶段的初始化没有问题:

  1. A designated initializer must ensure that all of the properties introduced by its class are initizlied before it delegates up to a superclass initializer.
  2. A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn't, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
  3. A convenience initializer must delegate to another initializer before assigning a value to any property. If it doesn't, the new value the convenience initializer assigns will be overwritten by its own class's designated initializer.
  4. An initializer cannot call any instance methods, read the value of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

Initializer Inheritance and Overriding

跟oc不同,Swift中的子类默认不继承父类的构造方法。当你为子类定义了一个构造方法,这个构造方法match一个父类的指定构造方法,你实际上是对那个指定构造方法进行了重写。因此,自己在子类构造方法前边用override标记。这个原则对于默认构造方法也适用。看个例子:

class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
} class Bicycle : Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}

Automatic Initializer Inheritance

正如上边提到的,默认情况下,子类不继承弗雷德构造方法。但是,在满足一些条件的前提下,父类的构造方法可以被自动继承。

假设你已经为子类新引入的属性提供了默认值,那么有下面两条规则适用:

  1. 如果子类没有定义任何指定构造方法,那么子类会自动继承父类所有的指定构造方法。
  2. 如果子类提供了父类所有指定构造方法(不论是规则1提到的,还是通过自定义实现),那么子类自动继承父类所有的便利构造方法。需要注意,子类可以把父类指定构造方法实现为便利构造方法,这样也可以满足这个条件

这些规则即使在子类添加了便利构造方法时,依然适用。

Designated and Convenience Initializer in Action

在这一部分,我们通过一个例子来解释上边的两条规则。

class Food {
var name: String
//指定构造方法
init(name: String) {
print("super versioin")
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) {
print("sub version")
self.init(name: name, quantity: 1)
}
}

现在这两个类的构造方法可以用下标的图来清楚的展示:

我们直接看一下RecipeIngredient类,首先有一个自定义的指定构造方法init(name:quantity:),然后有一个自定义的便利构造方法init(name:),这个方法实际上是重写了Food类的唯一的一个指定构造方法,所以满足了上面的第二条原则,因此自动继承Food类所有的便利构造方法,即init()方法。这里需要注意的是,RecipeIngredient继承来的init()方法中,调用的init(name:)不是Food版本的,而是RecipeIngredient版本的。

现在再来定义一个类:

class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) * \(name)"
output += purchased ? "✔️" : "❌"
return output
}
}

因为ShoppingListItem本身没有定义任何的构造方法,因此它从父类继承所有的构造方法。此时,示意图如下:

Swift: Initialization-1的更多相关文章

  1. iOS: 聊聊 Designated Initializer(指定初始化函数)

    iOS: 聊聊 Designated Initializer(指定初始化函数) 一.iOS的对象创建和初始化 iOS 中对象创建是分两步完成: 分配内存 初始化对象的成员变量 我们最熟悉的创建NSOb ...

  2. Swift 提示:Initialization of variable was never used consider replacing with assignment to _ or removing it

    Swift 提示:Initialization of variable was never used consider replacing with assignment to _ or removi ...

  3. Swift - 初始化Initialization

    Ps:苹果官方文档-Initialization 自定义控件初始化中常见的几种错误(指定构造器和便利构造器)截图:   意思是:1.没有添加重写符override(重写父类方法)2.没有重写initW ...

  4. Swift中懒加载(lazy initialization)的实现

    Swift中是存在和OC一样的懒加载机制的,但是这方面国内的资料比较少,今天把搜索引擎换成了Bing后发现用Bing查英文\最新资料要比百度强上不少. 我们在OC中一般是这样实现懒加载初始化的: 1: ...

  5. Lazy Initialization with Swift

    Lazy initialization (also sometimes called lazy instantiation, or lazy loading) is a technique for d ...

  6. Swift - 懒加载(lazy initialization)

    Swift中是存在和OC一样的懒加载机制的,在程序设计中,我们经常会使用 懒加载 ,顾名思义,就是用到的时候再开辟空间 懒加载 格式: lazy var 变量: 类型 = { 创建变量代码 }() 懒 ...

  7. Swift 的类、结构体、枚举等的构造过程Initialization(下)

    类的继承和构造过程 类里面的全部存储型属性--包含全部继承自父类的属性--都必须在构造过程中设置初始值. Swift 提供了两种类型的类构造器来确保全部类实例中存储型属性都能获得初始值,它们各自是指定 ...

  8. Swift学习笔记十四:构造(Initialization)

         类和结构体在实例创建时,必须为全部存储型属性设置合适的初始值. 存储型属性的值不能处于一个未知的状态.     你能够在构造器中为存储型属性赋初值,也能够在定义属性时为其设置默认值.下面章节 ...

  9. swift语言点评十六-Initialization && Deinitialization

    initial value:必须初始化.不影响观察者 Classes and structures must set all of their stored properties to an appr ...

  10. Swift 2 语言精要 - Initialization and Deinitialization

    init相当于构造函数 deinit相当于析构函数 class InitAndDeinitExample { // Designated (i.e., main) initializer init ( ...

随机推荐

  1. make xxx Is a directory. Stop.

    转自:make xxx Is a directory. Stop. 的原因 make xxx Is a directory. Stop. 的原因 编译内核时候的一个错误提示 make: ***    ...

  2. bootstrap的栅格布局不支持IE8该如何解决

    用bootstrap的栅格布局在IE8上出现失效的情况,通常有两种解决方式 方法/步骤   方法一:引用第三方js,一个叫respond.js的东西,github上可以搜到   方法二:由于IE8不支 ...

  3. [BZOJ 1081] [SCOI2005] 超级格雷码 【找规律】

    题目链接:BZOJ - 1081 备注:此题BZOJ上貌似没有 spj ,要把一般顺序的每个格雷码倒着输出...比如 0102 输出为 2010 题目分析 就是按照 Gray 码的生成方法写前几个出来 ...

  4. Google提议使用Jsonnet来增强JSON

    Google开源了一门配置语言Jsonnet来取代JSON,它完全向后兼容并加入了一些新特性:注释.引用.算术运算.条件操作符,数组和对象内含,引入,函数,局部变量,继承等.Jsonnet程序被翻译为 ...

  5. onchange事件

    一.onchange 一般input type text的onchange事件的触发需要两个条件:1.输入框的值发生了改变:2.该文本框失去了焦点,而真正的事件的触发却是发生在该文本框失去焦点的时候, ...

  6. MVC自学系列之三(MVC视图-Views)

    View的约定 1.根据约定,Views目录下包含着每一个与Controller同名但是没有像Controller后缀的文件夹:因此对于控制器HomeController就对应在views目录下有个目 ...

  7. xstream 别名的用法<转>

    1.xstream的alias使用方法: 1.1 作用:将序列化中的类全量名称,用别名替换. 1.2  使用方法:xstream.alias("blog", Blog.class) ...

  8. Ultraedit中使用Astyle格式化代码

    方法: 使用UE的自定义工具栏并借助开源工具astyle.exe来完成. 1. 首先下载最新的astyle,因为ue自带的astyle版本太老,不支持空格.中文名等. http://astyle.so ...

  9. Android开源项目发现--- 工具类快速开发篇(持续更新)

    1. Guava Google的基于java1.6的类库集合的扩展项目 包括collections, caching, primitives support, concurrency librarie ...

  10. 使用 Chef 管理 Azure 资源

     发布于 2014-07-08 作者 陈 忠岳 Chef 是一款开源的开发运营(DevOps)工具,用以应对 IT 基础设施方面的挑战.微软开放技术有限公司(简称"微软开放技术" ...