參考資料

感謝大大~~
https://itisjoe.gitbooks.io/swiftgo/content/
因應swift3,筆記內容與gitbook上略有不同

struct和enum是value type, class是reference type

  • struct宣告為常數,就無法更改內部的值,但class可以。
  • struct和enum這種value type,內部member function不能修改member data的值
  • 如果真的要修改要加上mutating修飾字
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
struct Apple{
    var x = 0.0
    mutating func add(num:Double){
        x += num
    }
}

var x = Apple(x:1.2)
x.add(1.5)
print(x.x)
  • mutating 還可以替換掉本身的實體結構
1
2
3
4
5
6
struct Apple{
    var x = 0.0
    mutating func new(num:Double){
        x = Apple()
    }
}

function注意事項

call function時,參數後要指定參數名稱,不可以省略

1
2
3
4
func plus(a :Int, b:Int){
  print(a+b)
}
plus(a:1, b:3)

如果要省略要加上底線

1
2
3
4
func plus(_ a :Int,_ b:Int){
  print(a+b)
}
plus(1, 3)

外部參數名稱(External parameter Name)

事實上a前面的底線是External parameter Name的意思。External parameter Name用於外部呼叫Function時的參數識別名,若不寫則與內部名稱相同。
而底線代表不在意參數識別名稱,即為可忽略的意思。

1
2
3
4
func plus(AAA a :Int,BBB b:Int){
  print(a+b)
}
plus(AAA:1, BBB:3)

型別方法

在class中要宣告class方法(非object方法),可以用關鍵字classstatic

1
2
3
4
5
6
class SomeClass {
    // 定義一個型別方法
    class func someTypeMethod() {
        print("型別方法")
    }
}

Closure

函式也是一種型別

型別名稱為
(參數型別)->回傳型別

化function為Clousure

  1. 刪掉func與函式名
  2. 將大括號移到最外側
  3. 在參數後面加上in

before

1
2
3
func add(x:Int, y:Int)->Int{
	return x + y
}

after

1
2
3
4
{
	(x:Int, y:Int)->Int in
  return x + y
}

Closure簡寫

  1. 如果已經知道參數跟回傳值的型別的話,可以省略Clousure裡的參數與回傳值型別

    1
    2
    
    	let numberArray=[1,3,6,8,10,45]
    numberArray.map( {(number) in return number+10} ) // 省略型別
  2. 如果Closure有回傳值,而且程式碼是一行的話,可以省略return,

    1
    2
    
    	let numberArray=[1,3,6,8,10,45]
    numberArray.map( {(number) in number+10} ) // 回傳number+10
  3. 可以用$0, $1 … 代表參數。

    1
    2
    
    	let numberArray=[1,3,6,8,10,45]
    numberArray.map( {$0 + 10} ) // 省略參數,利用$0代表參數
  4. 如果Closure是最後一個參數的話,可以把clousure寫在小括號的外面。

    1
    2
    
      let numberArray=[1,3,6,8,10,45]
      numberArray.map(){$0 + 10} // 寫在小括號的外面
  5. 如果Closure是唯一的參數的話,可以省略小括號 swift let numberArray=[1,3,6,8,10,45] numberArray.map{$0 + 10} // 直接省掉小括號

guard的用途

guard比if能更好的實行Defensive Programming,因為在guard中的可選綁定可以在接下來code中使用。

1
2
3
4
5
6
var text:String? = "abc"
if let apple = text{
	print(apple)
}
// 在這邊用會失敗
// print(apple)
1
2
3
4
5
6
var text:String? = "abc"
guard let apple = text else{
	print(apple)
}
// 照用
print(apple)

Error

Swift丟出的異常是一種enum,實作時繼承Error。
有幾種方法

  1. 最基本的do try catch
  2. 向上拋擲function填throw
  3. try? : 如果出現錯誤則回傳nil(不用再寫do catch),可搭配optinal binding
  4. try! : 一定成功。(若沒成功會crash)

init

init?

init?代表可以回傳nil,也就是可能init失敗。
參看下面的code

1
2
3
4
5
6
7
8
9
class Baby{
	var age:Int
	init?(age:Int){
  	if age < 0{
    	return nil
    }
  	self.age = age
  }
}

一個寶寶生出來小於一歲是不合理的,所以init失敗回傳nil

init的順序

  • 原則1:屬性的初始,必須在當初宣告屬性的類別裡進⾏。
  • 原則2:⼦類別得先完成⾃⼰屬性的初始後,才能進⾏⽗類別屬性的初始。
    • 此原則是為了防止繼承的class複寫基礎class的方法,而基礎的class使用此方法來初始化。
    • 若此function剛好動用到繼承class之porperty,那就會出現問題。

假設有一個基礎型別寶寶

1
2
3
4
5
6
class Baby{
    var age:Int
    init(age:Int){
        self.age = age
    }
}

正確的範例

1
2
3
4
5
6
7
class SuperBaby: Baby{
    var magic:Int
    init(age:Int, magic:Int){
        self.magic = magic
        super.init(age: age)
    }
}

錯誤的範例: 違反原則1,age屬性的初始化不在宣告的類別裡。

1
2
3
4
5
6
7
8
class SuperBaby: Baby{
    var magic:Int
    init(age:Int, magic:Int){
        self.magic = magic
        self.age = age
        super.init(age: age)
    }
}

錯誤的範例: 違反原則2,必須先將自己的屬性先初始化完畢,才能call super init

1
2
3
4
5
6
7
class SuperBaby: Baby{
    var magic:Int
    init(age:Int, magic:Int){
    		super.init(age: age)
        self.magic = magic
    }
}

正確的範例: 在age初始化後才改變age的值

1
2
3
4
5
6
7
8
class SuperBaby: Baby{
    var magic:Int
    init(age:Int, magic:Int){
        self.magic = magic
        super.init(age: age)
        self.age = age
    }
}

convenience init

有兩種類型的init

  • designated initializer : 一般的init
  • convenience initializer : 加了關鍵字convenience的init

擁有convenience關鍵字的init才可以call其他init function,來減少重複工作,非常方便
舉例來說:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Baby{
    var age:Int
    var name:String
    init(age:Int, name:String){
        self.age = age
        self.name = name
    }
    
    convenience init(name:String){
    		self.init(age:1, name:name)
    }
}

init的繼承

  • designated initializer的繼承
    • 子類別沒有定義任何designated initializer,預設全部自動繼承。
    • 子類別定義了任意init,不會自動繼承父類別的init。
  • convenience initializer的繼承
    • 子類別沒有定義任何designated initializer,預設全部自動繼承。
    • 子類別覆寫了父類別所有的designated initializer,則自動繼承所有convenience initializer。
    • 子類別複寫不完全,則不會繼承convenience initializer。

使用required防止init失傳