swift4.1 系統學習十三 結構體(二)
//
//main.swift
//swift14(結構體)
//
//Created by iOS on 2018/10/15.
//Copyright © 2018年 weiman. All rights reserved.
//
import Foundation
// 結構體(二)
/*
繼續上節內容,學習結構體的相關知識。
上一節的學習中,我們主要學習了結構體中的屬性,包括:
1.儲存式例項屬性
2.惰性儲存式屬性
3.計算式屬性
4.屬性觀察者
5.型別屬性
本節,我們來學習結構體中的方法,包括:
1.例項方法
2.型別方法
3.初始化器方法
4.逐成員的初始化器方法
5.值型別的初始化器代理
6.可失敗的初始化器
7.下標語法
*/
// 1. 例項方法
/*
定義:當我們在列舉、類、結構體型別中定義一個函式時,該函式被稱為“方法”。
每個例項方法都具有一個隱式的屬性 self ,它指向呼叫此方法的物件例項,所以self的型別為當前物件的型別。
*/
do {
struct Test { // 儲存屬性 var a = 10 let s = "hello" func printS() { // 這裡,self可以省略。 print("a = \(a)") print("s = \(s)") } // 由於這個方法中,對例項屬性進行了修改,所以需要加上mutating關鍵字。 mutating func method2(a: Int) { // 這裡的引數 a 與 例項屬性 a重名,所以需要加上self加以區分。 self.a += a + 1 } // 將關聯的物件例項重新修改為預設狀態。 mutating func method3() { self = Test() } } var test = Test() test.method2(a: 10) test.printS() test.method3() test.printS()
}
// 例項方法的引用
/*
由於例項方法必須要與某一物件例項進行關聯,所以我們用一個函式引用物件指向某一物件例項的方法時
需要將物件例項也一起帶上。
*/
do {
print("\n") struct Test { var property = 100 mutating func method(a: Int) { property += a } func foo(_ : Void = ()) { print("property = \(property)") } func foo(a: Int) { print("value = \(property + a)") } } var test = Test() test.property += 10 // 這裡通過method方法簽名來對它進行呼叫。 // 由於mutating方法不允許通過函式引用物件對它進行引用, // 所以這裡只能直接通過方法簽名做直接呼叫, // 這是允許的。 test.method(a:)(5) let ref = { test.method(a: 6) } ref() let ref1 = test.foo(_:) ref1(()) let ref2 = test.foo(a: ) ref2(10) /* 小結: 對例項方法的引用必須包含與它關聯的物件例項。 */
}
// 2. 型別方法
/*
型別方法與型別屬性類似,是與型別相關聯的方法,而不是物件例項。
定義一個型別方法也很簡單,直接在func 前面新增static就可以了。
如果當前型別是類型別,那麼我們還能使用class關鍵字修飾,表示當前型別方法能夠被子類重寫。
如果在類型別中用了static關鍵字修飾,那麼該型別方法不允許被子類重寫。
*/
do {
print("\n") struct Test { static var a = 100 /// 型別方法 static func method() { // 可以修改型別屬性 self.a += 20 print("method: a = \(a)") } static func getValue(a: Int) -> Int { return self.a + a } static func foo(_: Void) { print("這是一個foo") } /// 過載方法 static func foo(a: Int) { print("a = \(a)") } } // 呼叫型別方法method Test.method() let a = Test.getValue(a: 5) print("a = \(a)") var ref = Test.foo(_:) ref(()) let ref2 = Test.foo(a: ) ref2(1) /* 列印結果: method: a = 120 a = 125 這是一個foo a = 1 */
}
// 3. 初始化器方法
/*
初始化器方法用於在建立一個類、結構體或者列舉型別的物件例項時為該物件的例項屬性進行初始化。
在swift中使用init關鍵字表示當前型別的初始化器方法,然後後面跟著形參列表。
注意:
由於一個型別的初始化器方法肯定返回它所建立的物件例項,因此其返回型別不需要寫,就是當前型別本身。
*/
do {
struct Test { var a = 10 let b: Float var c: String var d: Int? init() { b = 1.0 self.c = "Hello" } } // 省略init var test = Test() test = Test.init() let ref = Test.init test = ref()
}
// 4. 逐成員的初始化器方法
/*
對於結構體型別,有一種預設的初始化形式,叫做逐成員的初始化器方法。
當我們在結構體中定義了一些儲存式屬性,並沒有對他們進行初始化,也沒有顯示的提供初始化器方法,那麼
我們在用該結構體去建立一個物件例項時就可以使用逐成員的初始化方法來為該結構體物件中的每個儲存式例項屬性
進行指定具體的值。
*/
do {
print("\n") struct Test: CustomStringConvertible { var a = 10 let b: Float var c: String var d: Int? var description: String { return "a = \(a), b = \(b), c = \(c), d = \(d)" } } // 逐成員的初始化器方法 let test = Test.init(a: 10, b: 1.9, c: "生活在社會底層,也要努力活著。", d: 8) print("test : \(test)")
}
// 5. 值型別的初始化器代理
/*
當我們在一個初始化器方法中呼叫另一個初始化器方法以執行對一個物件例項的部分初始化,那麼這個過程就
叫做初始化器代理。
*/
do {
print("\n") struct Test { var a = 10 let b: Float var c: String var d: Int? init(b: Float) { self.b = b c = "" } init(b: Float, c: String) { self.init(b: b) self.c = c } init(b: Float, c: String, d: Int) { self.init(b: b, c: c) self.d = d } } var test = Test.init(b: 1.0) test = Test.init(b: 2.0, c: "OK") print("test: \(test)") test = Test.init(b: 3.4, c: "哈啊哈哈", d: 8) print("test: \(test)")
}
// 6. 可失敗的初始化器
/*
有時候需要定義某些型別,這些型別根據使用者的輸入或者當前的執行環境可能造成其物件例項的建立失敗,此時
我們可以使用“可失敗的初始化器”。
可失敗的初始化器可根據當前條件返回空值。因此,當我們使用可失敗的初始化器來建立一個物件時,該物件的
型別為Optional型別。
*/
do {
print("\n") struct Test { var a: Int init? (value: Int) { if value == 0 { return nil } a = 100 / value } } let test = Test(value: 0) if test == nil { print("failed") } else { print("test: \(test)") }
}
// 7. 下標語法
/*
swift語言中,允許我們在自定義型別中使用下標。
*/
do {
print("\n") struct Test { // 儲存式例項屬性a var a = 10 // 定義下標方法 subscript(index: Int) -> Int { get { return a + index } set(value) { a = value + index } } subscript(str: String) -> Int { return str.count } subscript(value: Int, str: String) -> Int? { get { guard let count = Int(str) else { return nil } return value + count } set { if let data = newValue, let strValue = Int(str) { a = value + data + strValue } } } subscript(_: Void) -> Int { get { return a } set { a = newValue } } } var test = Test() /// 這裡呼叫了test物件的小標方法subscript(index: Int)的setter方法 test[1] = 10 print("test[5] = \(test[5])") //列印:test[5] = 16 print("count = \(test["abc"])") test[2, "123"] = 100 print("test: \(test)")
}
// 8. key path
/*
有時候,一個結構體、列舉或者類型別中的某個屬性的型別比較複雜,型別巢狀比較深,在swift4中,引入了
Smart KeyPaths這一概念來簡化對一些巢狀比較深的屬性訪問。
*/
do {
print("\n") struct MyRect { struct Point { var x: Float var y: Float } struct Size { var width: Float var height: Float } var position: Point var size: Size } struct MyStruct { var property: Int var rect: MyRect } /// 這裡用Smart KeyPath字面量 /// 定義一個widthKeyPath關鍵路徑, /// 它是對MyStruct.rect.size.width /// 這一例項屬性的訪問路徑 let widthKeyPath = \MyStruct.rect.size.width var obj = MyStruct(property: 10, rect: MyRect(position: MyRect.Point(x: 0.0, y: 1.0), size: MyRect.Size(width: 10.0, height: 20.0))) let width = obj[keyPath: widthKeyPath] print("width: \(width)") obj[keyPath: \MyStruct.rect.position.x] += obj.rect.position.y * 8.0 print("x = \(obj.rect.position.x)")
}