swift 学习- 15 -- 构造过程 01
// 构造过程 是使用类,结构体 或 枚举类型的实例之前的准备过程,
// 在新实例可用前必须执行这个过程, 具体操作包括 设置实例中每个存储型属性的初始值 和 执行其他必须的设置 或 初始化工作
// 通过定义 构造器 来实现构造过程, 这些构造器可以看做是用来创建特定类型新实例的 特殊方法, 与 OC 中的构造器不同, Swift 的构造器不需要返回值, 它们的主要任务是保证 新实例在第一次使用前完成正确的 初始化
// 类的实例也可以定义 析构器 在实例释放前执行 特定的清除工作
// 存储属性的初始赋值
// 类和结构体在穿件实例时, 必须为所有存储属性设置合适的初始值, 存储型属性的值不能处于 一个位置的状态
// 你可以在 构造器 中为存储型属性赋初值, 也可以在定义属性时 为其设置默认值
// 注意 : 当你为存储型属性设置默认值 或者 在构造器 中为其赋值时, 他们的值是被直接设置的, 不会触发任何属性观察者
// 构造器
// 构造器在创建某个 特定类型 的新实例时被调用, 他的最简形式类似于 一个不带任何参数的实例方法, 以关键字 init 命名
// init(){
//
// }
// 下面的例子中定义了一个用来保存华氏温度的结构体 Fahrenheit, 它拥有一个 Double 类型的存储属性 temperature :
struct Fahrenheit{
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature) Fahrenheit")
// 这个结构体定义了一个不带任何参数的 构造器 init ,并在里面将存储型属性 temperature 的值 初始化为 32.0
// 默认属性值
// 如前所述, 你可以在构造器中为存储型属性设置初始值, 同样, 你也可以在属性声明时为其设置默认值
// 注意 : 如果一个属性总是使用相同的初始值, 那么为其设置一个默认值, 那么为其设置一个默认值 比 每次都在构造器中赋值要好, 两种方法的效果是一样的,只不过 使用默认值 让属性的初始化 和 声明结合的更紧密. 使用默认值能让你的构造器更简洁, 更清晰, 且能通过 默认值自动推导出属性的类型, 同时, 它也能让你充分利用默认构造器, 构造器继承等特性 .
// 你可以使用更简洁的 方式在定义 结构体 Fahrenheit 时为属性 temperature 设置 默认值
struct Fahrenheit2{
var temperature = 32.0
}
// 自定义构造过程
// 你可以通过 输入参数 和 可选类型的属性 来自定义 构造过程, 也可以在构造过程中修改常量属性,
// 构造参数
// 自定义 构造过程 时, 可以在定义中提供构造函数, 指定所需值的类型 和 名字, 构造函数的功能 和 语法跟函数的方法的参数相同
// 下面的例子中定义了一个包含 摄氏度温度的 结构体 Celsius, 它定义了两个不同的构造器: init(fromFahrenheit:) 和 init(formKelvinL), 二者分别通过不同温标下的温度值来创建新的实例:
struct Clesius{
var temperatureInCelsius: Double
init(fromFahrenheit fahrenHeit: Double) {
temperatureInCelsius = (fahrenHeit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Clesius.init(fromFahrenheit: 212.0)
print(boilingPointOfWater.temperatureInCelsius)
let freezingPointOfWater = Clesius.init(fromKelvin: 273.15)
print(freezingPointOfWater.temperatureInCelsius)
// 第一个构造器拥有一个构造函数, 其外部名字为 fromFahrenheit , 内部名字为 Fahrenheit, 第二个构造器也拥有一个构造函数, 其外部名字是 fromKelvin , 内部名字是 kelvin , 这两个构造器都是讲唯一的参数值转换成摄氏温度值, 并保存在属性 temperatureInCe
// 参数的内部名称 和 外部名称
// 跟函数和方法参数相同, 构造参数也拥有一个在构造器内部使用的参数名字和一个在调用构造器时使用的外部参数名字
// 然而, 构造器并不像函数和方法那样在括号前有一个可辨别的名字, 因此在调用构造器时, 主要通过构造器中 参数名和类型 来确定应该被调用的构造器. 正因为参数如此重要, 如果你在定义构造器时 没有提供参数的外部名字, Swift 回味构造器自动生成一个跟内部名字相同的外部名
// 下面的例子中 定义了一个 Color 结构体, 它包含三个常量: red , green , blue, 这些属性可以存储 0.0 到 1.0 之间的值, 用来指示颜色中, 红, 绿, 蓝成分的含量
// Color 提供了一个构造器, 其中包含三个 Double 类型的参数, Color 也可以提供第二个构造器, 它只包含名为 white 的 Double 类型的参数, 它被用于给上述三个构造参数赋予同样的值
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.init(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color.init(white: 0.5)
// 注意 : 如果不用过外部参数名字传值, 你是没有办法调用这个构造器的, 只要这个都早起定义了某个外部参数的参数名, 你就必须使用它. 忽略它导致编译错误
// 不带外部名的构造器参数
// 如果你不希望为构造器的某个参数提供外部名字, 你可以使用下划线 (_) 来显式描述他的外部名, 一次重写上面所说的默认行为
struct Celsius{
var temperayureInCelsius: Double
init(fromF f: Double) {
temperayureInCelsius = (f - 32.0) / 1.8
}
init(fromK k: Double) {
temperayureInCelsius = k - 273.15
}
init(_ celsius: Double) {
temperayureInCelsius = celsius
}
}
let bodyTemperature = Celsius.init(37.0)
// 调用 Celsius(37.0) 意图明确, 不需要外部参数名称, 因此适合使用 init(_ celsius: Double) 这样的构造器, 从而可以提供 Double 类型的参数值调用构造器, 而不需要加上外部名称
// 可选属性类型
// 如果你定制的类型包含一个 逻辑上允许取值为空的存储型属性 -- 无论是因为它无法再初始化时赋值, 还是 因为它在之后某个时间点可以赋值为空 -- 你都需要将它定义为可选类型, 可选类型的属性自动初始化 为 nil , 表示这个属性是有意在初始化时 设置为空的
// 下面的例子定义了一个 SurveyQuestion, 它包含一个可选字符串属性 response:
class SurveyQuestion{
var text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let cheeseQuestion = SurveyQuestion.init(text: "Do you like cheese?")
cheeseQuestion.ask()
cheeseQuestion.response = "Yes. I do like cheese"
// 我们将 response 声明为 String? 或者说是 可选字符串类型, 当 SuveyQuestion 实例化时, 它将自动复制为 nil, 表明此字符串暂时还没有值
// 构造过程中常量属性的修改
// 你可以 在构造过程中的任意时间点 给常量属性指定一个值, 只要在构造过程结束时 是一个确定的值, 一旦常量属性被赋值, 它将永远不被改变
// 注意 : 对于类的实例来说, 它的 常量属性 只能在 它的类的构造过程中修改, 不能再子类中修改
// 你可以修改上面的 SurveyQuestion 实例, 用常量属性提到变量属性 text, 表示问题内容 text 在 SurveyQuestion 的实例被创建之后不会再被修改, 尽管 text 属性现在是常量, 我们任然可以 在类的构造器中设置它的值
class SueveyQuestions{
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(self.text)
}
}
// 在默认构造器中修改常量属性的值, 常量属性的值 不能已经设置默认值, 否则会报错
// 默认构造器
// 如果 结构体 或 类的所有属性 都有默认值, 同时没有自定义的构造器, 那么 Swift 会给这些 结构体 或 类提供一个 默认的构造器, 这个默认构造器将简单地创建 一个所有属性都设置默认值的实例
class ShoppingListItem{
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
// 由于 ShoppingListItem 类中的 所有的属性都有默认值, 且它是没有父类的基类, 他讲自动获得一个 可以为所有属性设置默认值的 默认构造器
// 结构体的逐一成员构造器
// 除了上面提到的默认构造器, 如果结构体没有自定义的构造器, 他们将自动获得一个逐一成员的构造器, 即使结构体的存储型属性没有默认值
// 逐一成员构造器使用来初始化结构体新实例成员属性的快捷方法, 我们在调用逐一成员构造器是, 通过与成员属性名相同的参数名进行传值 来完成 对成员属性的初始赋值
struct Size{
let ss = "44"
var width = 0.0, height = 0.0
}
let twoByTwo = Size.init(width: 2.0, height: 2.0)
// 值类型的构造器代理
// 构造器可以通过 调用其它构造器 来完成实例的部分构造过程, 这一过程称为 构造代理, 它能减少 多个构造器件的 代码重复
// 构造器代理的实现规则和形式 在 值类型 和 类类型中有所不同, 值类型 (结构体和枚举类型) 不支持继承, 所以构造器代理的过程相对简单, 因为他们只能代理给自己的其他构造器, 类则不同, 它可以继承自 其他类, 这个意味着类有责任保证其所有继承的存储属性 在构造是也能正确的初始化,
// 对于值类型, 你可以使用 self.init 在自定义的构造器中引用相同类型中其他构造器, 并且你只能在构造器背部调用 self.init
// 如果你为 某个值类型 定义了一个自定义的 构造器, 你将无法访问到默认构造器, 这种限制可以防止 你为 值类型增加一个额外的且 十分复杂的构造器之后, 任然有人错误的使用自动生成的构造器
// 下面的例子将定义一个结构体 Rect ,用来代表几何矩形. 这个例子需要两个辅助的 结构体 Size 和 Pointm 他们各自为其所有的属性提供了初始值 0.0
struct Point{
var x = 0.0,y = 0.0
}
// 你可以通过以下三种方式 Rect 创建实例 -- 使用被初始化为 默认值的 origin 和 size 属性来初始化, 或者指定的 origin 和 size 实例来初始化, 或者指定 center 和 size 来初始化,
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 / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x:originX,y:originY), size: size)
}
}
// 第一个 Rect 构造器 init(), 在功能上跟没有自定义构造器是自动获得的默认构造器是一样的, 这个构造器是一个空函数, 使用一对大括号 {} 来表示,它没有执行任何构造过程, 调用这个构造器将返回一个 Rect 实例, 他的 origin 和 size 属性都使用定义是的 默认值 Point(x:0.0,y:0.0) 和 Size(width:0.0,height:0.0)
let basicRect = Rect.init()
// 第二个 Rect 构造器 init(origin:size:), 在功能上跟结构体在没有自定义构造器时获得的 逐一成员构造器是一样的. 这个构造器只是简单地将 origin 和 size 的参数赋值给对应的存储类型
let originRect = Rect.init(origin: Point.init(x: 2.0, y: 2.0), size: Size.init(width: 5.0, height: 5.0))
// 第三个 Rect 构造器 init(center:size:) 稍微复杂一些, 它先通过 center 和 size 的值计算出 origin 的坐标, 然后再调用 (或者说代理给), init(origin:size:) 构造器来讲新的 origin 和 size 值赋值到对应的属性中:
let centerRect = Rect.init(center: Point.init(x: 4.0, y: 4.0), size: Size.init(width: 3.0, height: 3.0))
// 构造器 init(center:size:) 可以直接将 origin 和 size 的新值赋值到对应的属性中, 然后, 利用恰好提供了相关功能的现有构造器会 更为方便, 构造器init(center:size)的意图也会更加清晰