构造过程是为了使用某个类、结构体或枚举类型的实例而进行的准备过程。这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务。

Swift 构造函数使用 init() 方法。

与 Objective-C 中的构造器不同,Swift 的构造器无需返回值,它们的主要任务是保证新实例在第一次使用前完成正确的初始化。

类实例也可以通过定义析构器(deinitializer)在类实例释放之前执行清理内存的工作。


存储型属性的初始赋值

类和结构体在实例创建时,必须为所有存储型属性设置合适的初始值。

存储属性在构造器中赋值时,它们的值是被直接设置的,不会触发任何属性观测器。

存储属性在构造器中赋值流程:

  • 创建初始值。

  • 在属性定义中指定默认属性值。

  • 初始化实例,并调用 init() 方法。

构造器

构造器在创建某特定类型的新实例时调用。它的最简形式类似于一个不带任何参数的实例方法,以关键字init命名。

语法

init()
{
// 实例化后执行的代码
}

实例

以下结构体定义了一个不带参数的构造器 init,并在里面将存储型属性 length 和 breadth 的值初始化为 6 和 12:

struct rectangle {
var length: Double
var breadth: Double
init() {
length =
breadth =
}
}
var area = rectangle()
print("矩形面积为 \(area.length*area.breadth)")

以上程序执行输出结果为

矩形面积为 72.0

默认属性值

我们可以在构造器中为存储型属性设置初始值;同样,也可以在属性声明时为其设置默认值。

使用默认值能让你的构造器更简洁、更清晰,且能通过默认值自动推导出属性的类型。
struct rectangle {
// 设置默认值
var length =
var breadth =
}
var area = rectangle()
print("矩形的面积为 \(area.length*area.breadth)")

构造参数

你可以在定义构造器 init() 时提供构造参数,如下所示:

struct Rectangle {
var length: Double
var breadth: Double
var area: Double init(fromLength length: Double, fromBreadth breadth: Double) {
self.length = length
self.breadth = breadth
area = length * breadth
} init(fromLeng leng: Double, fromBread bread: Double) {
self.length = leng
self.breadth = bread
area = leng * bread
}
} let ar = Rectangle(fromLength: , fromBreadth: )
print("面积为: \(ar.area)") let are = Rectangle(fromLeng: , fromBread: )
print("面积为: \(are.area)")

内部和外部参数名

跟函数和方法参数相同,构造参数也存在一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字。

然而,构造器并不像函数和方法那样在括号前有一个可辨别的名字。所以在调用构造器时,主要通过构造器中的参数名和类型来确定需要调用的构造器。

如果你在定义构造器时没有提供参数的外部名字,Swift 会为每个构造器的参数自动生成一个跟内部名字相同的外部名

struct Color {
let 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 magenta = Color(red: 1.0, green: 0.0, blue: 1.0) print("red 值为: \(magenta.red)")
print("green 值为: \(magenta.green)")
print("blue 值为: \(magenta.blue)") // 创建一个新的Color实例,通过三种颜色的外部参数名来传值,并调用构造器
let halfGray = Color(white: 0.5)
print("red 值为: \(halfGray.red)")
print("green 值为: \(halfGray.green)")
print("blue 值为: \(halfGray.blue)")

没有外部名称参数

如果你不希望为构造器的某个参数提供外部名字,你可以使用下划线_来显示描述它的外部名。

struct Rectangle {
var length: Double init(frombreadth breadth: Double) {
length = breadth *
} init(frombre bre: Double) {
length = bre *
}
//不提供外部名字
init(_ area: Double) {
length = area
}
} // 调用不提供外部名字
let rectarea = Rectangle(180.0)
print("面积为: \(rectarea.length)") // 调用不提供外部名字
let rearea = Rectangle(370.0)
print("面积为: \(rearea.length)") // 调用不提供外部名字
let recarea = Rectangle(110.0)
print("面积为: \(recarea.length)")
面积为: 180.0
面积为: 370.0
面积为: 110.0

可选属性类型

如果你定制的类型包含一个逻辑上允许取值为空的存储型属性,你都需要将它定义为可选类型optional type(可选属性类型)。

当存储属性声明为可选时,将自动初始化为空 nil。

struct Rectangle {
var length: Double? init(frombreadth breadth: Double) {
length = breadth *
} init(frombre bre: Double) {
length = bre *
} init(_ area: Double) {
length = area
}
} let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)") let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)") let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")
面积为:Optional(180.0)
面积为:Optional(370.0)
面积为:Optional(110.0)

构造过程中修改常量属性

只要在构造过程结束前常量的值能确定,你可以在构造过程中的任意时间点修改常量属性的值。

对某个类实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。

尽管 length 属性现在是常量,我们仍然可以在其类的构造器中设置它的值:

struct Rectangle {
let length: Double? init(frombreadth breadth: Double) {
length = breadth *
} init(frombre bre: Double) {
length = bre *
} init(_ area: Double) {
length = area
}
} let rectarea = Rectangle(180.0)
print("面积为:\(rectarea.length)") let rearea = Rectangle(370.0)
print("面积为:\(rearea.length)") let recarea = Rectangle(110.0)
print("面积为:\(recarea.length)")

默认构造器

默认构造器将简单的创建一个所有属性值都设置为默认值的实例:

以下实例中,ShoppingListItem类中的所有属性都有默认值,且它是没有父类的基类,它将自动获得一个可以为所有属性设置默认值的默认构造器

class ShoppingListItem {
var name: String?
var quantity =
var purchased = false
}
var item = ShoppingListItem() print("名字为: \(item.name)")
print("数理为: \(item.quantity)")
print("是否付款: \(item.purchased)")

以上程序执行输出结果为:

名字为: nil
数理为:
是否付款: false

结构体的逐一成员构造器

如果结构体对所有存储型属性提供了默认值且自身没有提供定制的构造器,它们能自动获得一个逐一成员构造器。

我们在调用逐一成员构造器时,通过与成员属性名相同的参数名进行传值来完成对成员属性的初始赋值。

下面例子中定义了一个结构体 Rectangle,它包含两个属性 length 和 breadth。Swift 可以根据这两个属性的初始赋值100.0 、200.0自动推导出它们的类型Double。

struct Rectangle {
var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0) print("矩形的面积: \(area.length)")
print("矩形的面积: \(area.breadth)")

由于这两个存储型属性都有默认值,结构体 Rectangle 自动获得了一个逐一成员构造器 init(width:height:)。 你可以用它来为 Rectangle 创建新的实例。

以上程序执行输出结果为:

名字为: nil
矩形的面积: 24.0
矩形的面积: 32.0

值类型的构造器代理

构造器可以通过调用其它构造器来完成实例的部分构造过程。这一过程称为构造器代理,它能减少多个构造器间的代码重复。

以下实例中,Rect 结构体调用了 Size 和 Point 的构造过程:

struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
} struct Rect {
var origin = Point()
var size = Size()
init() {}
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
init(center: Point, size: Size) {
let originX = center.x - (size.width / )
let originY = center.y - (size.height / )
self.init(origin: Point(x: originX, y: originY), size: size)
}
} // origin和size属性都使用定义时的默认值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 结构体初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 结构体初始值: \(basicRect.origin.x, basicRect.origin.y) ") // 将origin和size的参数值赋给对应的存储型属性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0)) print("Size 结构体初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 结构体初始值: \(originRect.origin.x, originRect.origin.y) ") //先通过center和size的值计算出origin的坐标。
//然后再调用(或代理给)init(origin:size:)构造器来将新的origin和size值赋值到对应的属性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0)) print("Size 结构体初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 结构体初始值: \(centerRect.origin.x, centerRect.origin.y) ")

构造器代理规则

值类型 类类型
不支持继承,所以构造器代理的过程相对简单,因为它们只能代理给本身提供的其它构造器。 你可以使用self.init在自定义的构造器中引用其它的属于相同值类型的构造器。 它可以继承自其它类,这意味着类有责任保证其所有继承的存储型属性在构造时也能正确的初始化。

类的继承和构造过程

Swift 提供了两种类型的类构造器来确保所有类实例中存储型属性都能获得初始值,它们分别是指定构造器和便利构造器。

指定构造器 便利构造器
类中最主要的构造器 类中比较次要的、辅助型的构造器
初始化类中提供的所有属性,并根据父类链往上调用父类的构造器来实现父类的初始化。 可以定义便利构造器来调用同一个类中的指定构造器,并为其参数提供默认值。你也可以定义便利构造器来创建一个特殊用途或特定输入的实例。
每一个类都必须拥有至少一个指定构造器 只在必要的时候为类提供便利构造器
Init(parameters) {
statements
}
convenience init(parameters) {
statements
}

指定构造器实例

class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int // 新的子类存储变量
init(no1 : Int, no2 : Int) {
self.no2 = no2 // 初始化
super.init(no1:no1) // 初始化超类
}
} let res = mainClass(no1: )
let res2 = subClass(no1: , no2: ) print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

便利构造器实例

class mainClass {
var no1 : Int // 局部存储变量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
} class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 便利方法只需要一个参数
override convenience init(no1: Int) {
self.init(no1:no1, no2:)
}
}
let res = mainClass(no1: )
let res2 = subClass(no1: , no2: ) print("res 为: \(res.no1)")
print("res2 为: \(res2.no1)")
print("res2 为: \(res2.no2)")

构造器的继承和重载

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

父类的构造器仅在确定和安全的情况下被继承。

当你重写一个父类指定构造器时,你需要写override修饰符。

class SuperClass {
var corners =
var description: String {
return "\(corners) 边"
}
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)") class SubClass: SuperClass {
override init() { //重载构造器
super.init()
corners =
}
} let subClass = SubClass()
print("五角型: \(subClass.description)")

指定构造器和便利构造器实例

接下来的例子将在操作中展示指定构造器、便利构造器和自动构造器的继承。

它定义了包含两个个类MainClass、SubClass的类层次结构,并将演示它们的构造器是如何相互作用的。

class MainClass {
var name: String init(name: String) {
self.name = name
} convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Runoob")
print("MainClass 名字为: \(main.name)") let main2 = MainClass()
print("没有对应名字: \(main2.name)") class SubClass: MainClass {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
} override convenience init(name: String) {
self.init(name: name, count: )
}
} let sub = SubClass(name: "Runoob")
print("MainClass 名字为: \(sub.name)") let sub2 = SubClass(name: "Runoob", count: )
print("count 变量: \(sub2.count)")

类的可失败构造器

如果一个类,结构体或枚举类型的对象,在构造自身的过程中有可能失败,则为其定义一个可失败构造器。

变量初始化失败可能的原因有:

  • 传入无效的参数值。

  • 缺少某种所需的外部资源。

  • 没有满足特定条件。

为了妥善处理这种构造过程中可能会失败的情况。

你可以在一个类,结构体或是枚举类型的定义中,添加一个或多个可失败构造器。其语法为在init关键字后面加添问号(init?)。

实例

下例中,定义了一个名为Animal的结构体,其中有一个名为species的,String类型的常量属性。

同时该结构体还定义了一个,带一个String类型参数species的,可失败构造器。这个可失败构造器,被用来检查传入的参数是否为一个空字符串,如果为空字符串,则该可失败构造器,构建对象失败,否则成功。

struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
} //通过该可失败构造器来构建一个Animal的对象,并检查其构建过程是否成功
// someCreature 的类型是 Animal? 而不是 Animal
let someCreature = Animal(species: "长颈鹿") // 打印 "动物初始化为长颈鹿"
if let giraffe = someCreature {
print("动物初始化为\(giraffe.species)")
}

枚举类型的可失败构造器

你可以通过构造一个带一个或多个参数的可失败构造器来获取枚举类型中特定的枚举成员。

实例

下例中,定义了一个名为TemperatureUnit的枚举类型。其中包含了三个可能的枚举成员(Kelvin,Celsius,和 Fahrenheit)和一个被用来找到Character值所对应的枚举成员的可失败构造器:

enum TemperatureUnit {
// 开尔文,摄氏,华氏
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
} let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("这是一个已定义的温度单位,所以初始化成功。")
} let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("这不是一个已定义的温度单位,所以初始化失败。")
}

类的可失败构造器

值类型(如结构体或枚举类型)的可失败构造器,对何时何地触发构造失败这个行为没有任何的限制。

但是,类的可失败构造器只能在所有的类属性被初始化后和所有类之间的构造器之间的代理调用发生完后触发失败行为。

实例

下例子中,定义了一个名为 StudRecord 的类,因为 studname 属性是一个常量,所以一旦 StudRecord 类构造成功,studname 属性肯定有一个非nil的值。

    let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失败构造器") {
print("模块为 \(stname.studname)")
}

覆盖一个可失败构造器

就如同其它构造器一样,你也可以用子类的可失败构造器覆盖基类的可失败构造器。

者你也可以用子类的非可失败构造器覆盖一个基类的可失败构造器。

你可以用一个非可失败构造器覆盖一个可失败构造器,但反过来却行不通。

一个非可失败的构造器永远也不能代理调用一个可失败构造器。

实例

以下实例描述了可失败与非可失败构造器:

class Planet {
var name: String init(name: String) {
self.name = name
} convenience init() {
self.init(name: "[No Planets]")
}
}
let plName = Planet(name: "Mercury")
print("行星的名字是: \(plName.name)") let noplName = Planet()
print("没有这个名字的行星: \(noplName.name)") class planets: Planet {
var count: Int init(name: String, count: Int) {
self.count = count
super.init(name: name)
} override convenience init(name: String) {
self.init(name: name, count: )
}
}

输出:

行星的名字是: Mercury
没有这个名字的行星: [No Planets]

可失败构造器 init!

通常来说我们通过在init关键字后添加问号的方式(init?)来定义一个可失败构造器,但你也可以使用通过在init后面添加惊叹号的方式来定义一个可失败构造器(init!)。实例如下:

truct StudRecord {
let stname: String init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
} let stmark = StudRecord(stname: "Runoob")
if let name = stmark {
print("指定了学生名")
} let blankname = StudRecord(stname: "")
if blankname == nil {
print("学生名为空")
}

输出:

指定了学生名
学生名为空

Swift 构造过程的更多相关文章

  1. swift学习笔记之-构造过程

    //构造过程 import UIKit /* 构造过程(Initialization): 1.构造过程是使用类.结构体或枚举类型的一个实例的准备过程.在新实例可用前必须执行这个过程,具体操作包括设置实 ...

  2. Swift中类的两段式构造(类的构造过程)

    import Foundation /* 类的构造需要两个阶段: 第一阶段: 1.程序调用子类的某个构造器 2.为实例分配内存, 此时实例的内存还没有被初始化 3.指定构造器确保子类定义的所有实例存储 ...

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

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

  4. swift 学习- 16 -- 构造过程 02

    // 类的继承 和 构造过程 // 类里面的所有的存储型属性 -- 包括所有继承自父类的属性 -- 都必须在构造过程中设置初始值 // Swift  为类类型提供了 两种构造器来确保实例中所有的存储属 ...

  5. swift 学习- 15 -- 构造过程 01

    // 构造过程 是使用类,结构体 或 枚举类型的实例之前的准备过程, // 在新实例可用前必须执行这个过程, 具体操作包括 设置实例中每个存储型属性的初始值 和 执行其他必须的设置 或 初始化工作 / ...

  6. swift 笔记 (十四) —— 构造过程

    构造过程 为了生成类.结构体.枚举等的实例,而做的准备过程,叫做构造过程. 为了这个过程,我们一般会定义一个方法来完毕,这种方法叫做构造器.当然它的逆过程,叫做析构器,用于在实例被释放前做一些清理工作 ...

  7. swift学习笔记(五)构造过程

    构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.在构造过程中,对每一个属性进行了初始值预设和其它必要的准备和初始化工作. 与OC相比,swift的构造函数.不须要返回值.同一时候,在 ...

  8. Welcome-to-Swift-14构造过程(Initialization)

    构造过程是为了使用某个类.结构体或枚举类型的实例而进行的准备过程.这个过程包含了为实例中的每个属性设置初始值和为其执行必要的准备和初始化任务. 构造过程是通过定义构造器(Initializers)来实 ...

  9. SICP— 第一章 构造过程抽象

    SICP  Structure And Interpretation Of Computer Programs 中文第2版 分两部分  S 和 I 第一章 构造过程抽象 1,程序设计的基本元素 2,过 ...

随机推荐

  1. 利用jenkens+github实现简单的CI/CD

    一  获取github accessToken 依次点击 settings----> Developer settings --->Personal access tokens  到这里如 ...

  2. 我是怎么找到电子书的 - IT篇

    转自于:http://my.oschina.net/0757/blog/375908#OSC_h2_8 IT-ebooks http://it-ebooks.info/ http://www.it-e ...

  3. MacOS下安装unicorn这个库失败

    因为在Mac下安装pwntools,发现安装unicorn库的时候失败了,编译报错如下 make: *** [qemu/config-host.h-timestamp] Error 1 error: ...

  4. bloomberg bulkFile解析

    文章导航 bloomberg bulkfile解析 bloomberg bulkfile 在oracle的存储 准备工作: Bloomberg 提供了以下文件 1 . fields.csv 下载地址: ...

  5. 启动nginx 80端口被占用:tcp 0 0 127.0.0.1:80 127.0.0.1:34932 TIME_WAIT -

    1.启动nginx命令./sbin/nginx 2.提示80端口被占用 nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already ...

  6. Lua 学习之基础篇六<Lua IO 库>

    引言 I/O 库提供了两套不同风格的文件处理接口. 第一种风格使用隐式的文件句柄: 它提供设置默认输入文件及默认输出文件的操作, 所有的输入输出操作都针对这些默认文件. 第二种风格使用显式的文件句柄. ...

  7. python+requests----登录接口reponse中token传递给其他接口使用的一个简单小示例介绍

    #!/usr/bin/env python # coding=UTF-8 import requests def login(): url = "https://xxxx.xxx.xxx/v ...

  8. docker harbor 清理释放存储空间

    0.harbor界面端清理镜像 1.停止docker harbor docker-compose stop 2.预览运行效果 docker run -it --name gc --rm --volum ...

  9. python自动华 (六)

    Python自动化 [第六篇]:Python基础-面向对象   目录: 面向过程VS面向对象 面向对象编程介绍 为什么要用面向对象进行开发 面向对象的特性:封装.继承.多态 面向过程 VS 面向对象 ...

  10. 分页控件Webdiyer.MvcPager

    MVC 1.安装控件 install-package Webdiyer.MvcPager 2.Cotroller using System; using System.Collections.Gene ...