存储属性的初始赋值
类和结构体在创建实例时,必须为所有存储型属性设置合适的初始值。存储型属性的值不能处于一个未知的状态。可以在构造器中为存储型属性设置初始值,也可以在定义属性时分配默认值。
构造器
构造器在创建某个特定类型的新实例时被调用。构造器本身的作用,只是为了确保对象能被正确构造。
struct Fahreheit {
var temperature: Double
init() {
temperature = 32.0
}
}
var f = Fahreheit()
print("The default temperature is \(f.temperature)° Fahrenheit") //32
默认属性值
如果一个属性总是使用相同的初始值,那么为其设置一个默认值比每次都在构造器中赋值要好。
自定义构造过程
形参的构造过程
struct Celsius {
var temeratertureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temeratertureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temeratertureInCelsius = kelvin - 273.15
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
print(boilingPointOfWater.temeratertureInCelsius) //100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
print(freezingPointOfWater.temeratertureInCelsius) // 0.0
说明:第一个构造器拥有一个构造形参,其实参标签为 fromFahrenheit,形参命名为 fahrenheit;第二个构造器也拥有一个构造形参,其实参标签为 fromKelvin,形参命名为 kelvin。
形参命名和实参标签
//形参命名和实参标签
struct Color {
let red,green, blue: Double
init(red: Double, green: Double, blue: Double) { //red,green,blue为形参命名
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) { //white 为形参命名
self.red = white
self.green = white
self.blue = white
}
}
let magenta = Color(red:1.0, green: 0.0, blue: 1.0)
//let veryGreen = Color(0.0, 1.0, 0.0) //需要实参标签
let veryGreen = Color(white: 1.0)
不带实参标签的构造器形参
如果不希望构造器的某个形参使用实参标签,可以使用下划线(_)来代替显式的实参标签来重写默认行为。
//形参的构造过程
struct Celsius {
var temeratertureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temeratertureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temeratertureInCelsius = kelvin - 273.15
}
init(_ celsius: Double) {
temeratertureInCelsius = celsius
}
}
let bodyTemperature = Celsius(37.0) //37.0
说明:构造器调用 Celsius(37.0) 意图明确,不需要实参标签。因此适合使用 init(_ celsius: Double) 这样的构造器,从而可以通过提供未命名的 Double 值来调用构造器。
可选属性类型
//可选属性类型
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 swift?")
cheeseQuestion.ask() //Do you like swift?
cheeseQuestion.response = "Yes,I like it."
说明:当 SurveyQuestion 的实例初始化时,它将自动赋值为 nil,表明“暂时还没有字符“。
构造过程中常量属性的赋值
在构造过程中的任意时间点给常量属性赋值,只要在构造过程结束时它设置成确定的值。一旦常量属性被赋值,它将永远不可更改。
注:对于类的实例来说,它的常量属性只能在定义它的类的构造过程中修改;不能在子类中修改。
class SurveyQuestion {
let text: String
var response: String?
init(text: String) {
self.text = text
}
func ask() {
print(text)
}
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask() //How about beets?
let beetsQuestion1 = SurveyQuestion(text: "Do you like swift?")
beetsQuestion1.ask() //Do you like swift?
说明:用常量属性替代变量属性 text,表示问题内容 text 在 SurveyQuestion 的实例被创建之后不会再被修改。尽管 text 属性现在是常量,仍然可以在类的构造器中设置它的值。
默认构造器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
结构体的逐一成员构造器
逐一成员构造器是用来初始化结构体新实例里成员属性的快捷方法.
//结构体逐一构造器
struct Size {
var width = 0.0, height = 0.0
}
let twoBytwo = Size(width: 2.0, height: 2.0)
print(twoBytwo.width, twoBytwo.height)
值类型的构造器代理
构造器可以通过调用其它构造器来完成实例的部分构造过程,称为构造器代理.使用 self.init。有三种方法:
//方法1:使用含有默认值的 origin 和 size 属性来初始化
//方法2:将origin,size的实参赋值给对应的存储型属性
//方法3:通过center,size计算出originX,originY,然后调用init(origin:size:)构造器将新的origin,size值赋值到对应的属性中。
struct Rect {
var origin = PointOne()
var size = SizeTwo()
init() {} //origin,size使用默认值
init(origin: PointOne, size: SizeTwo) {
self.origin = origin
self.size = size
} //将origin,size的实参赋值给对应的存储型属性
init(center: PointOne, size: SizeTwo) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: PointOne(x: originX, y: originY), size: size)
} // 通过center,size计算出originX,originY,然后调用init(origin:size:)构造器将新的origin,size值赋值到对应的属性中。
}
//方法1
let basicRect = Rect()
print(basicRect.origin, basicRect.size) //PointOne(x: 0.0, y: 0.0) SizeTwo(width: 0.0, height: 0.0)
//方法2:
let originRect = Rect(origin: PointOne(x: 2.0, y: 2.0),
size: SizeTwo(width: 5.0, height: 5.0)) //originRect 的 origin 是 (2.0, 2.0),size 是 (5.0, 5.0)
//方法3
let centerRect = Rect(center: PointOne(x: 4.0, y: 4.0), size: SizeTwo(width: 3.0, height: 3.0))
print(centerRect.origin, centerRect.size) //PointOne(x: 2.5, y: 2.5) SizeTwo(width: 3.0, height: 3.0)
类的继承和构造过程
指定构造器(init())与便利构造器(convenience init())
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
let mysteryMeat = Food() //[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)
}
}
let oneMysteyItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
oneBacon.quantity //1
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
//继承RecipeIngredient的所有构造器(指定,便利)
class ShoppingListItemTwo: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? "√" : "x"
return output
}
}
var breakfastList = [ShoppingListItemTwo(),
ShoppingListItemTwo(name: "Bacon"),
ShoppingListItemTwo(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
print(item.description) //1 x Orange juice√, 1 x Baconx, 6 x Eggsx
}
类类型的构造器代理
为了简化指定构造器和便利构造器之间的调用关系,构造器之间的代理调用遵循以下三条规则(指定构造器必须总是向上代理,便利构造器必须总是横向代理):
- 指定构造器必须调用其直接父类的指定构造器。
- 便利构造器必须调用同类中定义的其它构造器。
- 便利构造器最后必须调用指定构造器。
两段式构造过程
类的构造过程包含两个阶段。第一个阶段,类中的每个存储型属性赋一个初始值。当每个存储型属性的初始值被赋值后,第二阶段开始,它给每个类一次机会,在新实例准备使用之前进一步自定义它们的存储型属性。
构造器的继承和重写
当重写一个父类的指定构造器时,总是需要写 override 修饰符,即使是为了实现子类的便利构造器。
//构造器的继承与重写
class Vehicle{
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)") //Bicycle: 2 wheel(s)
class Hoverboard: Vehicle {
var color: String
init(color: String){
self.color = color
}
override var description: String {
return "\(super.description) in a beautiful \(color)"
}
}
let hoverboard = Hoverboard(color: "silver")
print("Hoverboard: \(hoverboard.description)") //Hoverboard: 0 wheel(s) in a beautiful silver
子类可以在构造过程修改继承来的变量属性,但是不能修改继承来的常量属性。
构造器的自动继承
1.如果子类没有定义任何指定构造器,它将自动继承父类所有的指定构造器。2.如果子类提供了所有父类指定构造器的实现——无论是通过 1 继承过来的,还是提供了自定义实现——它将自动继承父类所有的便利构造器。
注:子类可以将父类的指定构造器实现为便利构造器来满足 2。
可失败构造器
如果类型转换不能保持值不变,则这个构造器构造失败。init?
//可失败构造器
struct Animal {
let species: String
init? (species: String){
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "Giraffe") //someCreature的类型是Animal?,而不是Animal
let anonymousCreature = Animal(species: "")
if anonymousCreature == nil {
print("The anonymous creature could not be initialized")
}
//针对数字类型转换的可失败构造器
let wholeNumber: Double = 12345.0
let pi = 3.1415926
if let valueMaintained = Int(exactly: wholeNumber){
print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")//12345.0 conversion to Int maintains value of 12345
}
let valueChanged = Int(exactly: pi) //valueChanged是Int?类型,不是Int类型
if valueChanged == nil {
print("\(pi) conversion to Int does not maintain value") //3.1415926 conversion to Int does not maintain value
}
枚举类型的可失败构造器
如果提供的形参无法匹配任何枚举成员,则构造失败。
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("This is a defined temperature unit, so initialization succeeded.")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}// “This is not a defined temperature unit, so initialization failed.”
带原始值的枚举类型的可失败构造器
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
}
}
}
enum TemperatureUnitTwo: Character {
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
}
let fahreheitUnit = TemperatureUnitTwo(rawValue: "F")
if fahreheitUnit != nil {
print("This is a defined temperature unit, so initialization succeeded.")
}
let unknownUnit = TemperatureUnitTwo(rawValue: "X")
if unknownUnit == nil {
print("This is not a defined temperature unit, so initialization failed.")
}
构造失败的传递
无论是向上代理还是横向代理,如果你代理到的其他可失败构造器触发构造失败,整个构造过程将立即终止,接下来的任何构造代码不会再被执行。
class Product {
let name: String
init?(name: String) {
if name.isEmpty{ return nil}
self.name = name
}
}
class CarItem: Product {
let quantity: Int
init?(name: String, quantity: Int){
if quantity < 1 {
return nil
}
self.quantity = quantity
super.init(name: name)
}
}
重写一个可失败构造器
在子类中重写父类的可失败构造器,用子类的非可失败构造器重写一个父类的可失败构造器。这可以定义一个不会构造失败的子类,即使父类的构造器允许构造失败。当用子类的非可失败构造器重写父类的可失败构造器时,向上代理到父类的可失败构造器的唯一方式是对父类的可失败构造器的返回值进行强制解包。注:可以用非可失败构造器重写可失败构造器,但反过来却不行。
class Document {
var name: String?
init(){}
init?(name: String){
if name.isEmpty {
return nil
}
self.name = name
}
}
class AutomaticallyNamedDocument: Document {
override init() {
super.init()
self.name = "[Untitled]"
}
override init(name: String) {
super.init()
if name.isEmpty {
self.name = "[Untitled]"
}else {
self.name = name
}
}
}
class UnititledDocument: Document {
override init() {
super.init(name: "Untitled")!
}
}
init! 可失败构造器
通过在 init 关键字后添加问号的方式(init?)来定义一个可失败构造器,但也可以通过在 init 后面添加感叹号的方式来定义一个可失败构造器(init!),该可失败构造器将会构建一个对应类型的隐式解包可选类型的对象。可以在 init? 中代理到 init!,反之亦然。也可以用 init? 重写 init!,反之亦然。还可以用 init 代理到 init!,不过,一旦 init! 构造失败,则会触发一个断言。
必要构造器
在类的构造器前添加 required 修饰符表明所有该类的子类都必须实现该构造器。在子类重写父类的必要构造器时,必须在子类的构造器前也添加 required 修饰符,表明该构造器要求也应用于继承链后面的子类。在重写父类中必要的指定构造器时,不需要添加 override 修饰符。
class SomeClass {
required init(){
}
}
class SomeSubclass: SomeClass{
required init() {
}
}
通过闭包或函数设置属性的默认值
如果某个存储型属性的默认值需要一些自定义或设置,可以使用闭包或全局函数为其提供定制的默认值。每当某个属性所在类型的新实例被构造时,对应的闭包或函数会被调用,而它们的返回值会当做默认值赋值给这个属性。
这种类型的闭包或函数通常会创建一个跟属性类型相同的临时变量,然后修改它的值以满足预期的初始状态,最后返回这个临时变量,作为属性的默认值。
class Someclass {
let someProperty: SomeType = {
return somevalue
}()
}
注意闭包结尾的花括号后面接了一对空的小括号,如果忽略相当于闭包本身作为值赋值给了属性,而不是将闭包的返回值赋值给属性。
如果使用闭包来初始化属性,请记住在闭包执行时,实例的其它部分都还没有初始化。这意味着不能在闭包里访问其它属性,即使这些属性有默认值。同样,也不能使用隐式的 self 属性,或者调用任何实例方法。
//通过闭包或函数设置属性的默认值 棋盘
struct chessboard {
var boardcolors :[Bool]{
var temporaryBoard = [Bool]()
var isBlack = false
for _ in 1...8 {
for _ in 1...8 {
temporaryBoard.append(isBlack)
isBlack = !isBlack
}
isBlack = !isBlack
}
return temporaryBoard
}()
func squareIsBlackAt(row: Int, column: Int) -> Bool {
return boardcolors[(row * 8) + column]
}
}
析构过程
析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用。析构器用关键字 deinit 来标示,类似于构造器要用 init 来标示。
在类的定义中,每个类最多只能有一个析构器,而且析构器不带任何参数和圆括号。
deinit{
}
析构器是在实例释放发生前被自动调用的。不能主动调用析构器。子类继承了父类的析构器,并且在子类析构器实现的最后,父类的析构器会被自动调用。即使子类没有提供自己的析构器,父类的析构器也同样会被调用。
因为直到实例的析构器被调用后,实例才会被释放,所以析构器可以访问实例的所有属性,并且可以根据那些属性可以修改它的行为.