Swift 基礎語法筆記
Contents
參考資料
感謝大大~~
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修飾字
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 還可以替換掉本身的實體結構
struct Apple{
var x = 0.0
mutating func new(num:Double){
x = Apple()
}
}
function注意事項
call function時,參數後要指定參數名稱,不可以省略
func plus(a :Int, b:Int){
print(a+b)
}
plus(a:1, b:3)
如果要省略要加上底線
func plus(_ a :Int,_ b:Int){
print(a+b)
}
plus(1, 3)
外部參數名稱(External parameter Name)
事實上a前面的底線是External parameter Name的意思。External parameter Name用於外部呼叫Function時的參數識別名,若不寫則與內部名稱相同。
而底線代表不在意參數識別名稱,即為可忽略的意思。
func plus(AAA a :Int,BBB b:Int){
print(a+b)
}
plus(AAA:1, BBB:3)
型別方法
在class中要宣告class方法(非object方法),可以用關鍵字class或static
class SomeClass {
// 定義一個型別方法
class func someTypeMethod() {
print("型別方法")
}
}
Closure
函式也是一種型別
型別名稱為
(參數型別)->回傳型別
化function為Clousure
- 刪掉func與函式名
- 將大括號移到最外側
- 在參數後面加上in
before
func add(x:Int, y:Int)->Int{
return x + y
}
after
{
(x:Int, y:Int)->Int in
return x + y
}
Closure簡寫
如果已經知道參數跟回傳值的型別的話,可以省略Clousure裡的參數與回傳值型別
let numberArray=[1,3,6,8,10,45] numberArray.map( {(number) in return number+10} ) // 省略型別
如果Closure有回傳值,而且程式碼是一行的話,可以省略return,
let numberArray=[1,3,6,8,10,45] numberArray.map( {(number) in number+10} ) // 回傳number+10
可以用$0, $1 … 代表參數。
let numberArray=[1,3,6,8,10,45] numberArray.map( {$0 + 10} ) // 省略參數,利用$0代表參數
如果Closure是最後一個參數的話,可以把clousure寫在小括號的外面。
let numberArray=[1,3,6,8,10,45] numberArray.map(){$0 + 10} // 寫在小括號的外面
如果Closure是唯一的參數的話,可以省略小括號
swift let numberArray=[1,3,6,8,10,45] numberArray.map{$0 + 10} // 直接省掉小括號
guard的用途
guard比if能更好的實行Defensive Programming,因為在guard中的可選綁定可以在接下來code中使用。
var text:String? = "abc"
if let apple = text{
print(apple)
}
// 在這邊用會失敗
// print(apple)
var text:String? = "abc"
guard let apple = text else{
print(apple)
}
// 照用
print(apple)
Error
Swift丟出的異常是一種enum,實作時繼承Error。
有幾種方法
- 最基本的do try catch
- 向上拋擲function填throw
- try? : 如果出現錯誤則回傳nil(不用再寫do catch),可搭配optinal binding
- try! : 一定成功。(若沒成功會crash)
init
init?
init?代表可以回傳nil,也就是可能init失敗。
參看下面的code
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,那就會出現問題。
假設有一個基礎型別寶寶
class Baby{
var age:Int
init(age:Int){
self.age = age
}
}
正確的範例
class SuperBaby: Baby{
var magic:Int
init(age:Int, magic:Int){
self.magic = magic
super.init(age: age)
}
}
錯誤的範例: 違反原則1,age屬性的初始化不在宣告的類別裡。
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
class SuperBaby: Baby{
var magic:Int
init(age:Int, magic:Int){
super.init(age: age)
self.magic = magic
}
}
正確的範例: 在age初始化後才改變age的值
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,來減少重複工作,非常方便。
舉例來說:
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。