[D.2]對Swift語法整理的非常好,這裏主要基於這個文檔提取出Swift相對於C/C++,Objective-C而言相對突出的語法。
不需要(加上也沒錯)在語句結尾加分號了(這個習慣可能改起來比較麻煩);與此同時,兩個語句最好要寫兩行了~,非要寫在一行的話,就要添加分號來分割了。
原生類型(值類型)
Bool
Int Int8 Int16
UInt UInt8 UInt16
Float
Double
nil
Void
可選類型
如果可選類型有值,它不等於nil;否則就是nil。
var Name: String? = "some name value"
如果知道可選值是有值的,可以強制解析:
if Name != nil {
var anotherName = Name!
}
可選綁定
if let constantName = someOptional {
statements
}
隱式解析可選類型
在可選類型值的後面加一個!符號,表示確定該值一定不爲nil,可以當作非可選類型用。這個時候就說該值是隱式解析可選類型。
但是如果該值爲nil的話,會導致運行時錯誤。所以要小心確保有值。
數值類型字面量
二進制 0b開頭
八進制 0o開頭
十六進制 0x開頭
數值較大,數字較多的時候,可以使用下劃線分割:982_803_020_12.022_329
類型別稱
typealias NewIntType = UInt16
基本運算符
兼容C的
賦值運算符沒有返回值
空合運算符
a??b
解釋:a != nil ? a! : b
區間運算符
a...b
解釋:[a, b]
a..<b
解釋:[a, b)
Unicode標量
let blackHeart = "\u{2665}"
let sparklingHeart = "\u{1F496}"
集合
數組 Arrays
創建空數組 let emptyAry = [Int]()
數組賦空 emptyAry = []
支持+、+=操作符,連接兩個數組
集合 Sets
集合的成員必須是支持哈希的,所有基本類型都是可哈希的
集合創建,訪問,修改
集合的操作
集合的成員關係
字典 Dictionaries
字典的鍵值必須是支持哈希的
字典的創建,訪問,修改
簡單值
使用let來聲明常量,使用var來聲明變量。
使用方括號[]來創建數組和字典,並使用下標或者鍵(key)來訪問元素。
值永遠不會被隱式轉換爲其他類型。如果你需要把一個值轉換成其他類型,請顯式轉換。
有一種更簡單的把值轉換成字符串的方法:把值寫到括號中,並且在括號之前寫一個反斜槓。
控制流
使用if和switch來進行條件操作,使用for-in、for、while和do-while來進行循環。包裹條件和循環變量括號可以省略,但是語句體的大括號是必須的。
switch 支持任意類型的數據以及各種比較操作——不僅僅是整數以及測試相等。
元組
可以作爲函數的返回值,參數……
函數
一等公民,而且可以在函數定義內部嵌套函數定義,函數可以直接作爲參數傳遞,當然這就使用到了函數原型。
函數的本質是一個命名的閉包。
也有一種匿名閉包,直接使用大括號包起來的可執行代碼片段。
類和對象
構造器init
析構器deinit
類和結構體的區別是,類對象傳引用,結構體對象傳值。
枚舉
可以自定義方法
協議
protocol
泛型的支持
錯誤處理
throws
try
斷言
assert()
控制流
for-in
while condition {
}
repeat {
} while condition
if condition {
}
switch value {
case value 1:
response to value 1
case value 1:
response to value 2
default:
default response
}
沒有break
如果需要多種情況合併,可以把標籤用逗號分隔放在一個case中。
case value1, value2:
statements
區間匹配
case 1 ..< 5
statement
元組匹配
case (0, 0):
statements
case (_, 1):
statements
元組的值綁定
值綁定的條件分類 where
控制轉移語句
continue
break
fallthrought
配合swift中的switch-case完成C語言中的自動執行;該語句放在case語句的最後一句;
return
throw
guard-else
不同於if-else,guard必須有一個else
API監測
swift內置支持API監測,防止代碼在不合適的機器上執行
格式: if #available()
函數
元組作爲返回值的時候,可以使用可選類型:(type1, type2, ...)?
函數參數包含:參數標籤,參數名,參數類型
func FuncName(參數標籤1 參數名1:參數類型1, ……){
}
不想要參數標籤的時候,可以使用_表示省略。
調用方使用參數標籤
函數內部使用參數名稱+類型
參數標籤是函數名稱的一部分。
swift中默認參數是常量,如果要修改需要在參數名之前添加inout關鍵字
函數類型
由參數類型+返回類型決定
這一點不同於C/C++的函數
可以使用類型推斷來聲明函數類型的變量。
嵌套函數
定義於函數內部,可用於外圍函數的返回類型,參數,以及調用。
閉包
下面是越來越省略的寫法
典型閉包寫法:
var names = ["ustone", "jhon", "mick"];
var reverseNames = names.sorted(by: {(s1:String, s2:String) -> Bool in
return s1 > s2
})
根據上下文推斷:不需要寫類型了,參數列表的括號也省略了
reverseNames = names.sorted(by: {s1, s2 in return s1 > s2})
單表達式閉包隱式返回:省略return
reverseNames = names.sorted(by: {s1, s2 in s1 > s2})
參數名稱省略:
reverseNames = names.sorted(by: {$0 > $1})
運算符方法:
reverseNames = names.sorted(by: >)
print(reverseNames)
尾隨閉包
用於:要將一個閉包作爲函數的最後一個參數傳入時
像這種事典型的閉包調用:
reverseNames = names.sorted(by: {$0 > $1})
尾隨閉包的調用:
reverseNames = names.sorted(){$0 > $1}
如果調用函數只有一個參數,那麼括號也可以省略:
reverseNames = names.sorted{$0 > $1}
閉包中的值捕獲
值捕獲可以保證所捕獲的值在超出作用域之後不消失。
閉包本身是引用類型
即使爲同一個閉包使用多個變量保存,並調用,最終始終是操作一個東西
逃逸閉包
在函數之外調用,一般用於先保存,後使用的情景;需要爲這類閉包添加@escaping關鍵字
自動閉包
自動創建,見於無參數的閉包類型。自動閉包可用於延時計算。
枚舉 (值類型)
一般情況下使用全路徑的枚舉;如果可以推斷出來可以使用簡潔的方式:.開頭的形式
遞歸枚舉
類和結構體
類屬於引用類型,傳參的時候計數自增
結構體屬於值類型,傳參的時候拷貝
結構體的初始化,可以使用按序初始化的方式
類實例是否引用了同一個實例,使用等價判斷===,以及不等價判斷!==;==用來判斷實例的值是否一樣
屬性
存儲屬性:類或者結構體的實例中的一個常量或者變量
計算屬性:setter & getter,如果只有getter,就是隻讀計算屬性了,如果是隻讀的話,可以省略get,花括號這樣的符號了
結構體實例如果賦於一個常量,那麼其屬性即使是var類型,也不可以被修改(由結構體是常量決定);類實例者不然。
延遲屬性,在定義的時候,在屬性聲明前加上lazy關鍵字;用於初始化需要大量計算,但是又可能用不到的數據。具體初始化時間在第一次訪問的時候。lazy不支持線程安全。
屬性觀察器:willset,didset;不過延遲屬性不能添加屬性觀察器
類型屬性:類似C語言的靜態常量,屬於多個類實例共享一個屬性值的屬性;使用static關鍵字,還可以使用class關鍵字,表示子類可以重寫。
可選類型屬性
方法
結構體和枚舉類型是值類型,默認方法不可以修改屬性;如果想要這樣做的話,需要聲明方法爲mutating。還可以在可變方法中爲self賦值(這在C++中是不可想象的)。
類型方法 在func前面加static;也可以在func前加class,表示子類可以重寫;類型方法中的self表示類型本身,類型方法內部調用其他類型方法不需要加類型限定
下標
subscript
重寫
必須加override,不然報錯;可以通過super來訪問超類的方法
計算屬性也可以被重寫;不過不能擴展只讀屬性爲讀寫屬性
屬性觀察器也可以重寫
下標訪問也可以重寫
防止重寫 final,可以作用於上面的所有重寫類型
構造過程
目標是保證新實例在使用前被設置正確初始化
存儲屬性的初始化可以在定義的時候,也可以在構造器中
由於swift可以在定義屬性的時候賦值,所以構造器功能相比C++的構造函數功能偏弱
可選屬性類型:構造器中不用初始化它
常量被賦值的地方也就是在定義的時候,以及在構造器中
默認構造器:如果每一個屬性都有默認值就會有一個;結構體中如果沒有定義構造器的話,就會有一個逐一成員構造器(類似C中的初始化結構體方式)
構造器代理:值類型和類類型兩種;前者不支持繼承,後者支持繼承。可以在init中調用self.init的其他形式
構造器分爲:指定構造器和便利構造器,後者是在init前面加上convenience關鍵字。在繼承結構中,前者向上調用,後者水平調用且必須最後調用到一個指定構造器。
子類重寫與超類同型的構造器的時候,也需要加override關鍵字,即使是分屬指定構造器和便利構造器兩類也需要加override
默認情況下,子類不會繼承父類的構造器;但是如果子類沒有定義構造器,就會繼承;如果子類重寫了父類的所有指定構造器,那麼還會自動繼承父類的便利構造器
可失敗構造器,在init後面加?,內部使用return nil表示構造失敗;此類對象,在使用前需要判斷是否爲nil;子類可以重寫父類的可失敗構造器,也可以使用一個非可失敗構造器來重寫父類的可失敗構造器。
另一種可失敗構造器,init!(...),失敗之後會引發一個斷言
必要構造器:required init(...),子類重寫的時候必須加required關鍵字
使用閉包設置屬性默認值
析構函數
deinit,無參數,無返回值,一個類最多一個原型定義
強引用類型 & 弱引用類型和無主引用類型
相互包含的兩個對象之間使用強引用的話,會產生無法釋放的問題;
弱引用類型是在聲明的時候加上weak關鍵字;弱引用對象被設置爲nil的時候,屬性觀察器不會被觸發;弱引用總是可選類型。
無主引用,關鍵字unowned,僅用於類,或者類作用域的協議類型。
防止出現強引用類型造成循環引用的情況有3種:
1. 二者皆不依賴對方存在
一強一弱引用的方式解決
2. 其中一個依賴另一方,反之則不然
一強一無主引用的方式解決
3. 彼此依賴對方的存在,共存亡
無主引用配合隱式解析可選類型屬性(Type!)
閉包作爲類的屬性引起的循環引用,是由於閉包屬於引用類型,閉包內使用類的屬性,必須使用self來顯式限定,這樣也可以提醒你引用了類實例要引起注意
使用捕獲列表:方括號內成對出現的參數,weak/unowned類型+類實例/初始化過的類實例屬性
那麼使用weak還是unowned?
閉包本身和捕獲的實例/屬性同時消亡的話,使用unowned來修飾被捕獲對象
被捕獲的引用實例/屬性可能爲nil的時候,使用weak來修飾被捕獲對象
被捕獲的引用實例/屬性不可能爲nil的時候,使用unowned來修飾被捕獲對象
可選鏈式調用
可選鏈式調用返回的是可選類型,不管多少層
訪問屬性
訪問下標
訪問可選類型的下標
可選鏈式調用可以簡化條件判斷,這一點比之前的語言是一大特色
錯誤處理
try、try?、try!:try表示後面的函數可能拋出異常,try?會在拋出異常的時候將返回值轉爲可選類型,try!表示禁用錯誤傳遞,在你確定不會拋出異常的時候使用,不過一旦有異常拋出的話,就會有一個運行時斷言錯誤。
throwing函數:帶有throws關鍵字的函數,構造器可也以是throwing函數
do-catch控制流,相當於C++中的try-catch
可以配合defer語句做好資源釋放
類型轉化
is:用來做類型檢查
as:用來做向下轉化;條件形式as? 可選綁定,強制形式as!
Any:任意不確定類型,包括可選類型,函數類型,閉包;可用來定義數組,裝載不一致類型;當你用一個Any類型表示一個可選值的時候,會有一個警告,這時候可以使用as Any來消除警告
AnyObject:任意不確定類類型實例
嵌套類型
這一點C++中也有,類內部可以定義類,並且可以多層嵌套。
擴展
可以對已有的類,結構體,枚舉類型,協議類型添加新的計算型屬性,計算型類型屬性,(可變mutating)實例方法,類型方法,新的(便利)構造器,下標(subscript),嵌套類型,使一個已有的類型符合某協議
擴展只是添加,不能重寫已有方法;並且不能添加存儲類型屬性
協議
protocol關鍵字代替class定義類類型時候的位置。
不指定屬性是存儲類型還是計算類型,只管屬性的類型和名稱
總是用var聲明變量屬性,使用static,class來修飾類型屬性
定義實例方法和類方法的時候,不需要寫方法體,當然是可以寫的。
mutating方法:實現mutating方法的時候,如果是類類型,不需要加該關鍵字,結構體和枚舉類型是必須要加的。mutating表示該方法可能會修改屬性的值
構造器:
協議中可以定義構造器這一點比較新穎;實現類中的init前必須加required關鍵字。
可失敗構造器:init?,init!
協議可以當作類型來使用
協議可以繼承協議,並支持多繼承
類類型專屬協議:繼承的時候在繼承列表的第一位上寫class,如果非類類型實現該協議,會有編譯時錯誤
協議合成:使用&連接兩個或多個協議類型,用來約束表示某實例(比如說做形參的時候)必須實現了這幾個協議
檢查協議一致性:is,as
可選協議:一般在需要和objective-c交叉的時候使用,需要在協議前加上@objc關鍵字;實現可選協議類型的時候,其屬性、方法會變成可選類型;
協議的擴展:在協議中定義並實現方法,避免在多個實現類中實現多份同樣的代碼,或者是爲了提供一個默認的實現;也可以使用where限制
範型
在類,方法,協議名稱後緊跟一對尖括號,裏面放上泛型名稱。
類型約束:指定一個類型參數必須繼承自指定類,符合一個特定的協議或者協議組合;也是放在尖括號中,使用冒號+類/協議名稱的方式。
關聯類型:定義協議的時候,預留一個類型佔位符,待實現的時候通過typealias指定具體類型。這有點像C++中的typedef的味道
where語句:放在方法體前面,用來限定泛型類型滿足某一個條件。
訪問控制
需要從模塊和源文件兩個級別看,從高到低依次是:open,public,internal,fileprivate,private
自定義類型:
元組:分量中最嚴格的部分決定
函數類型:參數和返回值中最嚴格的部分決定函數的訪問級別
枚舉類型:不能單獨指定枚舉成員的訪問級別
原始值和關聯值:都不能低於枚舉類型的訪問級別
嵌套類型:private級別的類型中的嵌套類型,自動擁有private訪問級別;public,internal級別的類型中的嵌套類型自動擁有internal訪問級別;想要嵌套類型擁有public級別,需要顯式指定
子類:不能高於父類;不過,可以通過重寫繼承來的任意類成員(方法,屬性,構造器,下標)來將訪問級別提高
成員的訪問級別:常量,變量,屬性,下標的訪問級別不能高於類型本身
Getter&Setter:可以顯式指定
構造器:默認構造器與類型的訪問級別一致,但最高是internal;除非顯示指定爲public
協議:定義協議的時候指定訪問級別也就是成員的,這一點不同於類類型;採納了協議的類型最終的訪問級別取定義類型的時候指定的類型和協議本身二者之中嚴格的那一個
擴展:對類,結構體,枚舉類型進行擴展,默認是保持訪問級別不變,但是可以顯式指定來修改擴展成員的訪問級別;通過擴展實現的協議,就不能顯示指定訪問級別類,會沿用協議本身的訪問級別
泛型:取決於泛型類型、泛型函數本身的訪問級別,再結合類型參數的類型約束
類型別名:不高於其表示的類型
高級運算符
運算符重載:二元符號,前綴prefix,後綴postfix,複合賦值運算符,等價,以及自定義運算符;這裏會定義爲類類型的成員。
Doc
[D.1] GitHub上的項目 swift-compiler-crashes 專注於swift編譯器崩潰。
[D.2] 《The Swift Programming Language》中文版
[D.3] 最詳盡的 Swift 代碼規範指南
[D.4] 使用 guard 的正確姿勢