學習之前你要有所準備,它不僅會花費你的一些時間,也許還會花費你的一些錢:

首先:你需要一個蘋果電腦,MacBook、MacBook Pro、Mac mini都可以,MacBook Air是不行的,記住。如果你買的是iMac、iMac Pro、Mac Pro的話,請務必留下聯繫方式。土豪,我們交個朋友吧!錢能解決的問題都不是問題,記得有錢就買好一點的。如果你現在啥都買不起,哈哈,那你只能等到下一次了,在基於C#開發Micrsoft程序時再見,那個程序也叫「iWriter」。其次:你需要安裝Xcode,這個不要錢。聽我的,點擊系統左上角「?」,選擇下拉菜單中「App Store...」,在跳出的App Store窗體的左上角里搜索「Xcode」,找到名為「Xcode」的程序點擊"獲取",等待安裝完成。

這裡,我們即將迎來iWriter的開發之戰。在吹響衝鋒號之前,首先我們需要了解Swift5.1。對,你沒看錯,我們這裡用的是Swift5.1,並會及時使用最新版本。所以你我要同步,因為大部分代碼都需要你試著運行在Xcode。

來吧,啟動Xcode:打開Xcode,選擇「Get started with a playground」,選擇「macOS」下的「Blank」,在下一步里選定保存位置,就可以了。我們開始鑄造王者之劍。

如果,你有其它程序的開發經驗,了解本章你只需要看下面10點。

本章講的是函數編程部分。枚舉、結構體、類下一章再講。

  1. let聲明常量。

let π:Double = 3.14

2. 未賦值不可使用。

var name:String // 變數聲明
print(name)
// error: variable name used before being initialized

3. 使用字元串用雙引號或3個雙引號,注意是3個雙引號。

使用3個雙引號包裹字元串,能保留字元串的格式。如宿進,分行

在使用3個雙引號的方式中,如果某行的行末有「」符號,那表示下一行與本行是連接在一起的。

字元串字面量內,可通過「(value)」格式插入變數或常量值。類似PHP的$value.

var str = "Jiang Youhua"
var words = """
(str) is a good person.
He is working very
hard.
"""
print(words)
// Prints Jiang Youhua is a good person.
// He is working very hard.
var _ = 3+4 //

4. for 語句不同,使用for-in , 提供「...」、「..<」表示遍歷的範圍。

let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
// TODO
}
for i in 1...5 {
// TODO
}
for i in stride(from: 0, through: 12, by: 4) {
print(i)
}

5. do-while改為repeat-while。do用在錯誤捕捉上do-catch。

6. switch語句可以使用範圍,各case默認為break,使用關鍵詞"fallthrough"確定流入下case。

let score:Int = 95
switch a {
case 100:
// TODO
case 95, 96, 97, 98, 99:
// TODO
case 80..<95:
// TODO
case 62..<80:
// TODO
case 60, 61:
// TODO
fallthrough
default:
// TODO
}

7. 提供Tuple。訪問Tuple元素,可通過鍵或索引。

let result = ("Bad request", 400) // result.0 is Bad request
let person = (name: "JiangYouhua", gender: true, age:12) // person.age is 12

8. 除Array,Dictionary之外,提供了Set。Set是同類元素的集合,不保存相同值。

var threeNumbers = [1, 3, 2] // Array
var aliasesOfCities:Dictionary = ["昆明":"春城", "上海":"魔都",] // Dictionary
var subjects:Set = ["Chinese", "English", "Mathematics", "English"] // Set
print(subjects) // Prints ["English", "Mathematics", "Chinese"]

9. Function保留了Objective-C的特徵,調用時需要參數名。但為了與閉包協調,提供添參數別名功能,這時調用函數使用的是參數別名。然後又提供「_」符表示匆略,使用它作參數別名,調用函數可忽略參數名。

說了那麼多被繞進去了,看代碼吧

// 函數
func point(x:Int, y:Int)->(String, Bool){
if x < 0 || y < 0 {
return ("Err", false)
}
return ("Point.x = (x), Point.Y = (y)", true)
}
point(x:100, y:200) //(.0 "Point.x = 100, Point.Y = 200", .1 true)

// 參數使用別名
func pointOfLayout(width x:Int, height y:Int)->(String, Bool){
if x < 0 || y < 0 {
return ("Err", false)
}
return ("Point.x = (x), Point.Y = (y)", true)
}
pointOfLayout(width: 240, height: 160) //(.0 "Point.x = 240, Point.Y = 160", .1 true)

// 參數忽略別名
func pointOfWord(_ x:Int, _ y:Int)->(String, Bool){
if x < 0 || y < 0 {
return ("Err", false)
}
return ("Point.x = (x), Point.Y = (y)", true)
}
pointOfWord(320, 129) // (.0 "Point.x = 320, Point.Y = 129", .1 true)

// 可變參數
func income(_ money:Double...){
var d = 0.0
for m in money {
d += m
}
print("The money added up to (d)")
}
income(1,2,2,2,5,5) // Prints The money added up to 17.0

10. 閉包、尾隨閉包、閉包轉義。

閉包的格式:{ (parameters) -> return type in statements }

/** 1. 定義一個使用閉包的函數 **/
func pointOfLayout(point:(_ x:UInt32, _ y:UInt32)->String) {
let x = arc4random() % 1280
let y = arc4random() % 960
let a = point(x, y)
print(a)
}

/** 2. 常規調用閉包,參考上面第5點 **/
pointOfLayout(point: {"point.x = ($0), point.y = ($1)"})
// Prints point.x = 1181, point.y = 382

/** 3. 尾隨調用諸包,一種語法糖形式 **/
pointOfLayout{"point.x = ($0), point.y = ($1)"}
// Prints point.x = 1101, point.y = 531

/** 4. 當閉包未在函數內調用時,需要使用閉包轉義,使用關鍵字:@escaping **/
var closures: [() -> Void] = []
func notCalledClosure(closure: @escaping () -> Void) {
closures.append(closure) // 沒有被調用,保存起來了
}

當然,你也可以跟我一起,從零開始,這個就比較長一點。

下面的swift學習教程,是精簡版,是本項目開發中所必需要了解的。如果你想成為Swift的高手,那你需要《葵花寶典》,請上官網查找:[The Swift Programming Language]。

當你看到這裡,還沒回頭,那表明你需要進一步的學習。所以確認已經打開了你的Xcode,我們將邊做邊學。下面的代碼你都需要輸入你的Playground里,運行看看。

滑鼠移到行號上,點擊你看到的播放圖標,就可以運行當前行以上的代碼。

一、注釋:分單行、多行、多行套用三種。

// 這是單行注釋

/*
這是多行注釋的第一行
這是多行注釋的第二行
*/

/* 多行套用注釋,外部
code
/* 多行套用注釋,內部 */
code
多行套用注釋,外部 */

二、關鍵字,請不要使用下列英文單詞作為自已在Swift編程中的命名。

與聲明有關:

  • class,類
  • deinit,析構函數
  • enum,枚舉
  • extension,拓展類
  • func,函數
  • import。引入模塊
  • init,構造函數
  • let,常量
  • protocol,協議
  • static,靜態
  • struct,結構體
  • subscript,下標語法
  • typealias,類型別名
  • var 變數

與語句有關:

  • if else,條件語句
  • for in where,循環語句
  • repeat while,循環語句
  • switch case default fallthrough,分支語句
  • do catch,錯誤捕捉
  • break,退出當前塊
  • continue,中斷當前流程
  • return,返回退出當前塊

表達式和類型:

  • __COLUMN__ ,當前列
  • __FILE__ ,當前文件名
  • __FUNCTION__ ,當前方法名
  • __LINE__ ,當前行
  • as,類型檢查
  • dynamicType,已廢除
  • is,類型檢查
  • new,無作用
  • super,指向父類實例
  • self,指向本類實例
  • Self,指向本類及其派生類實例
  • Type,指向本類

在特定上下文中使用:

associativity didSet get infix inout left mutating none nonmutating operator override postfix precedence prefix rightset unowned unowned(safe) unowned(unsafe) weak willSet

三、運算符

算術運算符:加、減、乘、除、求余。

let a = 1 + 2 // 加法運算 a is 3
let b = 2 - 1 // 減法運算 b is 1
let c = 5 * 2 // 乘法運算 c is 10
let d = 5 / 2 // 除法運算 d is 2, 整數相除只保留整數部分
let e = 5 % 2 // 求余運算 e is 1, 求兩個數的餘數

比較運算符:等於、不等於、大於、小於、大於或等於、小於或等於。

let a = 1 == 2 // 等於運算,a is false
let b = 1 != 2 // 不等於運算,b is true
let c = 1 > 2 // 大於運算,c is false
let d = 1 < 2 // 小於運算,d is true
let e = 1 >= 2 // 大於或等於運算,e is false
let f = 1 <= 2 // 小於或等於運算,f is true

邏輯運算符:邏輯與、邏輯或、邏輯非。

let a = true && false // 邏輯與運算,a is false
let b = true || false // 邏輯或運算,b is true
let c = !true // 邏輯非運算,c is false

位運算符:按位與、按位或、按位異或、按位反、按位左移、按位右移。

/** 為UInt8添加轉二進位字元串 **/
extension UInt8 {
private func pad(s : String, toSize: Int) -> String {
var padded = s
for _ in 0..<toSize - s.count {
padded = "0" + padded
}
return padded
}

// 二進位String
func binaryStr() -> String {
return "0b" + self.pad(s: String(self, radix: 2), toSize: 8)
}
}

/** 按位與運算,按位,同1取1,否取0 **/
let a:UInt8 = 0b11111100 & 0b00111100
print(a.binaryStr())
// 0b11111100
// 0b00111100
// -----------
// Prints 0b00111100

/** 按位或運算,按位,有1取1,否取0 **/
let b:UInt8 = 0b11111100 | 0b00111100
print(b.binaryStr())
// 0b11111100
// 0b00111100
// -----------
// Prints 0b11111100

/** 按位異或運算,按位,異取1,同取0 **/
let c:UInt8 = 0b11111100 ^ 0b00111100
print(c.binaryStr())
// 0b11111100
// 0b00111100
// -----------
// Prints 0b11000000

/** 按位反運算,按位,1轉0,0轉發1 **/
let d:UInt8 = ~(0b00001111)
print(d.binaryStr())
// 0b00001111
// -----------
// Prints 0b11110000

/** 按位左移運算,按指定數左移位,右以0補位 **/
let e:UInt8 = (0b11111100) << 2
print(e.binaryStr())
// 0b00111100
// -----------
// 0b0011110000, 左移後,左溢出,取右邊8位
// Prints 0b11110000

/** 按位左移運算,按指定數右移位,左以0補位 **/
let f:UInt8 = (0b11111100) >> 2
print(f.binaryStr())
// 0b00111100
// -----------
// 0b0011110000, 右移後,右清除,留左邊8位
// Prints 0b00111111

賦值運算。

var a:Int = 2
var b:Int = 5
b += a // b = b + a
b -= a // b = b - a
b *= a // b = b * a
b /= a // b = b / a
b %= a // b = b % a
b &= a // b = b & a
b |= a // b = b | a
b ^= a // b = b ^ a
b <<= a // b = b << a
b <<= a // b = b >> a

其他運算符。

/** 一元運算符:+、- **/
var x = 4
var y = 5
print(+x, -y)
// Prints 4 -5

/** 三元運算符:boolCondition ? a : b **/
var s = x > y ? "Yes" : "No"
print(s)
// Prints No

四、聲明與賦值:let用於常量,var用於變數

常量:是固定值,程序中不可改,聲明時須賦值。使用let聲明。

變數:非固定值,程序中可改,未賦值不可用。使用var聲明。

let π:Double = 3.14 // 常量
let e = 2.718 // 變數
var city:String = "daye"
var (x, y, z) = (100, 50, 45)
var lastName, firstName:String
lastName = "youhua"; firstName = "jiang"

五、內置數據類型:整數、浮點數、布爾、字元、字元串

整數:有Int、Int8、Int16、Int32、Int64,UInt、UInt8、UInt16、UInt32、UInt64計十類。

其中UInt、UInt8、UInt16、UInt32、UInt64五類為無符號整型。注意:Int、UInt的長度與系統位數相關,如:64位系統為64位,32位系統為32位。

let a = 17 // 十進位
let b = 0b10001 // 二進位
let c = 0o21 // 八進位
let d = 0x11 // 十六進位

浮點數:Float、Double。Float為32位,Double為64位。

布爾:只有兩個布爾常量,true 和 false。字元:單個字元,使用雙引號。字元串:多個字元,使用雙引號或3個雙引號賦值。
  • 字元串:賦值示例。

let name:String = "JiangYouhua"
let city:String = String("daye")

/** 使用3個雙引號賦值,字元串格式將保留 **/
let poem = """
床前明月光,疑是地上霜。
舉頭望明月,低頭思故鄉。
"""

/** 使用3個雙引號賦值,通行尾添加「」,表示合併下一行 **/
let proverb = """
A rolling stone gathers
no moss.
"""
// proverb is A rolling stone gathers no moss.

  • 字元串:混合。

/** +、+=, 拼合字元串 **/
var name:String = "You" + "Hua" // name is YouHua
name += " Jiang" // name is YouHua Jiang

/** (),將常量、變數添加到字元串 **/
let say = "(name) is good man" // say is YouHua Jiang is good man

/** %@ 格式化字元串 **/
String(format: "String is %@", "abc") // s1 is String is abc

/** %d 格式化Int **/
String(format: "Int is %d", 2) // Int is 2
String(format: "Int is %04d", 2) // Int is 0002

/** %f、%g 格式化Double **/
String(format: "Double is %f", 1.1) // Double is 1.100000
String(format: "Double is %0.2f", 1.1) // Double is 1.10
String(format: "Double is %g", 1.1) // Double is 1.1

  • 字元串:常用方法。

var slogan = "A rolling stone gathers no moss."

/** 字元串長 **/
var i = slogan.count // i is 32

/** 字元串比較 **/
var b1 = slogan == "Jiang Youhua" // b1 is false

/** 字元串是否為空 **/
var b2 = slogan.isEmpty // b2 is false

/** 判斷是否包含另一個字元串 **/
var b3 = slogan.contains("stone") // b3 is true

/** 分割 **/
var words = slogan.split(separator: " ") // words is ["A", "rolling", "stone", "gathers", "no", "moss."]

/** 合併 **/
var s1 = words.joined(separator: "_") // s1 is A_rolling_stone_gathers_no_moss

/** 獲取指定字元 **/
let char = slogan[slogan.index(slogan.startIndex, offsetBy: 8)] // char is g

/** 取部分 **/
let start = slogan.index(slogan.startIndex, offsetBy: 3)
let end = slogan.index(slogan.startIndex, offsetBy: 12)
let sub1 = slogan[start...end] // sub1 is olling sto
let sub2 = slogan.prefix(1) // sub2 is a
let sub3 = slogan.suffix(2) // sub3 is s.

/** 查找位置 **/
let r:Range = slogan.range(of: "no")!
let location = slogan.distance(from: slogan.startIndex, to: r.lowerBound) // location is 24

/** 改變大小寫 **/
let s2 = slogan.uppercased() // s2 is A ROLLING STONE GATHERS NO MOSS.
let s3 = slogan.lowercased() // s3 is a rolling stone gathers no moss."
let s4 = slogan.capitalized // s4 is a rolling stone gathers no moss."

/** 查找字元串 **/
let s5 = slogan.replacingOccurrences(of: ".", with: "!") // s5 is A rolling stone gathers no moss!"

六、內置集合類型:Tuple元組、Array數組、Set集合、Dictionary字典。

Tuple,元組是不同數據類型的集合,可通過下標或關鍵字獲取元素。

let result = ("Bad request", 400) // result.0 is Bad request
let person = (name: "JiangYouhua", gender: true, age:12) // person.age is 12

Array,數組是同類型數據是集合,可通過下標獲取元素。

var scores = [Int]() // scores is []
var names: [String] = ["Jiāngyǒuhuá", "Lǐbái", "Dùfǔ"] // names[0] is Jiāngyǒuhuá
var intTwo = Array(repeating: 88, count: 2) // stringTwo is [88, 88]
var threeNumbers = [1, 3, 2] // threeNubers[1] is 2

  • Array:常用方法。

var scores1 = [1, 4, 3, 5]
var scores2 = [7,3, 8, 2]

/** 合併 **/
var scoresAll = scores1 + scores1 // scoresAll is [1, 4, 3, 5, 1, 4, 3, 5]

/** 數組長度 **/
let i = scoresAll.count // i is 8

/** 數組是否為空 **/
let b1 = scores1.isEmpty // b1 is false

/** 添加元素 **/
scoresAll.append(9) // scoresAll = [1, 4, 3, 5, 1, 4, 3, 5, 9]

/** 刪除 **/
scoresAll.remove(at: 1)
print(scoresAll) // Prints [1, 3, 5, 1, 4, 3, 5, 9]

/** 排序 **/
let a1 = scoresAll.sorted(by: <) // a3 is [1, 1, 3, 3, 4, 5, 5, 9]

/** 過濾 **/
let a2 = scoresAll.filter{ $0 > 4 }
print(a2) // Prints [5, 5, 9]

/** 包含 **/
let b2 = scoresAll.contains(4) // b2 is true

Set,集合同類型數據的集合:無序,無索引,同值元素只保留一個。

var names = Set<String>() // names is []
var heights:Set<Double> = [175.5, 164.8, 180.2]
var subjects:Set = ["Chinese", "English", "Mathematics", "English"]
// subjects is ["English", "Mathematics", "Chinese"]

  • Set:常用方法。

var scores1:Set = [11, 4, 3, 0, 5,8]
var scores2:Set = [4, 3, 5]
var scoresUni = scores1.union(scores2) // scoresUni is [1, 0, 5, 8, 4, 3]
var scoresInt = scores1.intersection(scores2) // scoresInt is [3, 5, 4}]
var scoresSub = scores1.subtracting(scores2) //scoresSub is [11, 8, 0]
var scoresDif = scores1.symmetricDifference(scores2) // scoresDif is [11, 8, 0]

/** 數組長度 **/
let i = scores1.count // i is 6

/** 數組是否為空 **/
let b1 = scores1.isEmpty // b1 is false

/** 添加元素 **/
scoresUni.insert(9)
print(scoresUni) // Prints [4, 9, 5, 8, 11, 0, 3]

/** 刪除 **/
scoresUni.remove(1)
print(scoresUni) // Prints "[4, 9, 5, 8, 11, 0, 3]

/** 排序 **/
let a1 = scoresUni.sorted(by: <) // a3 is [0, 3, 4, 5, 8, 9, 11]

/** 過濾 **/
let a2 = scoresUni.filter{ $0 > 4 }
print(a2) // Prints [5, 5, 9]

/** 比較 **/
let b2 = scores1 == scores2 // 是否完全相同 b2 is false
let b3 = scores1.isSubset(of: scores2) // 1是否為2的子集 b3 is false
let b4 = scores1.isSuperset(of: scores2) // 1是否為2的父集 b4 is true
let b5 = scores1.isStrictSubset(of: scores2) // 1是否為2的子集並不相等 b5 is false
let b6 = scores1.isStrictSuperset(of: scores2) // 1是否為2的父集並不相等 b6 is true
let b7 = scores1.isDisjoint(with: scores2) // 是否沒有交集 b7 is false

Dictionary,字典。

var namesOfUsers = Dictionary<String, String>() // userNames is [:]
var argesOfUsers:Dictionary<String, Int> = [:] // userAges is [:]
var slogansOfUsers = [String:String]() // userSlogans is [:]
var heightsOfNames:Dictionary<String, Double> = ["Ada":165.5, "Basia":182.4,]
var aliasesOfCities:Dictionary = ["昆明":"春城", "上海":"魔都",]

  • Dictionary:常用方法。

var aliasesOfCities:Dictionary = ["昆明":"春城", "上海":"魔都",]

/** 計數 **/
let i = aliasesOfCities.count // i is 2

/** 添加 **/
aliasesOfCities["大冶"] = "銅都"
print(aliasesOfCities) // Prints ["上海": "魔都", "大冶": "銅都", "昆明": "春城"]

/** 刪除 **/
aliasesOfCities.removeValue(forKey: "上海")
print(aliasesOfCities) // Prints ["大冶": "銅都", "昆明": "春城"]

七、控制語句:控制語分循環語句、條件語句。

  1. 循環語句:for-in,while/repeat-while。
  2. 條件語句:if/if-else/if-else if-else,switch。
  3. 關鍵詞:break,continue。

for-in,循環語句。下面示例遍曆數組、字典、取值範圍、字元串。

/** Array **/
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names {
// TODO
}

/** Dictionary **/
let numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
for (animalName, legCount) in numberOfLegs {
// TODO
}
for (_, legCount) in numberOfLegs {
// TODO
}
// 下橫杠符號表示,放棄該值

/** Rang **/
for i in 1...5 {
// TODO
}
for _ in -5...0 {
// TODO
}
for i in 0..<10 {
// TODO
}
for i in stride(from: 0, through: 12, by: 4) {
print(i)
}
// Prints 0, 4, 8, 12

/** String **/
let s:String = "JiangYouhua"
for c in s {
print(c)
}
// Prints J, i, a, n, g, Y, o, u, h, u, a

while,repeat-while,循環語句。

var i = 0
while i < 4 {
// TODO
}
repeat {
// TODO
} while i > 0

if, if-else,if-else if-else,條件語句。

let score = 95
if score > 60 {
// TODO
}

if score > 60 {
// TODO
} else {
// TODO
}

if score < 60 {
// TODO
} else if score < 85 {
// TODO
} else {
// TODO
}

switch,分支語句。

let score = 95
switch a {
case 100:
// TODO
case 95, 96, 97, 98, 99:
// TODO 流到該節點
case 80..<95:
// TODO
case 65..<80:
// TODO
case 60, 61, 62, 63, 64:
// TODO
fallthrough
default:
// TODO
}

continue:中止本次流程。

break:中止區塊內流程。return:中止區塊內流程,可返回數據。

for i in 0...5 {
if i == 3 {
continue
}
print(i)
}
// Prints 0, 1, 2, 4, 5

for i in 0...5 {
if i == 3 {
break
}
print(i)
}
// Prints 0, 1, 2

for i in 0...5 {
if i == 3 {
return
}
print(i)
}
// Prints 0, 1, 2

八、Function函數:支持多參數、多返回值、參數默認值,提供參數別名、忽略參數名調用函數。確立函數的類型

函數的形式,下面羅列了8種

/** 1. 無參數,無返回值 **/
func helloWorld(){
print("Hello, World!")
}
helloWorld()
// Prints Hello, World!

/** 2. 有參數,無返回值 **/
func sayHello(name:String){
print("Hello, (name)!")
}
sayHello(name:"Jiang")
// Prints Hello, Jiang!

/** 3. 無參數,有返回值 **/
func averageScoreOfChinese()->Double{
return 89.9
}
print(averageScoreOfChinese())
// Prints 89.9

/** 4. 多參數,多返回值 **/
func score(subject:String, student:Int)->(String, Double){
return ("The Score of (subject) of (student)th student is", 99.9)
}
var result = score(subject:"English", student:120)
print(result.0, result.1)
// Prints The Score of English of 120th student is 99.9

/** 5. 預設參數,命名返回值 **/
func scoreOther(subject:String, student:Int = 10)->(info:String, score:Double){
return ("The Score of (subject) of (student)th student is", 99.9)
}
var state = scoreOther(subject:"English")
print(state.info, state.score)
// Prints The Score of English of 10th student is 99.9

/** 6. 參數別名 **/
func sayHelloAlias(alias name: String) {
print("Hello, (name)!")
}
sayHelloAlias(alias: "Jiang")
// Prints Hello, Jiang!

/** 7. 忽略參數名稱調用 **/
func point(_ x:Int, _ y:Int){
print("point.x = (x), point.y = (y)")
}
point(100, 200)
// point.x = 100, point.y = 200

/** 8. 可變參數 **/
func incomeOfUser(_ name:String, _ income:Double...){
var f = 0.00;
for i in income {
f += i
}
print("(name) has a total income of (f)")
}
incomeOfUser("Jiang", 100.00, 360.00, 80.50)
// Prints Jiang has a total income of 540.5

函數類型:函數的參數和返回數據的類型都相同的函數,為同一類型的函數。

忽略參數名稱的函數,可以作變數值、參數、返回值。

/** 1. 定義函數 **/
func point(_ x:Int, _ y:Int) -> Bool{
print("point.x = (x), point.y = (y)")
return true
}

/** 2. 函數作為變數值 **/
var aPoint:(Int, Int)-> Bool = point
aPoint(100, 90)
// Prints point.x = 100, point.y = 90

var iPoint = point
iPoint(200, 230)
// Prints point.x = 200, point.y = 230

/** 3. 函數作為參數值 **/
func pointOfLayout(point:(Int, Int)->Bool, width:Int, height:Int){
point(width, height)
}
pointOfLayout(point:aPoint, width:360, height:240)
// Prints point.x = 360, point.y = 240

/** 4. 函數作為返回值 **/
func xAxle(_ i:Int)->String{
return "point.x = (i)"
}
func yAxle(_ i:Int)->String{
return "point.y = (i)"
}
func axle(b:Bool)->(Int)->String{
return b ? xAxle : yAxle
}
var f = axle(b:false)
print(f(100))
// Prints point.y = 100

九、Closure閉包:可以看成加了語法糖的函數。格式 { (parameters) -> return type in statements }

閉包及其幾種簡用方式。

/***
sorted(by:)方法是Swift基本庫的方法,用來對集合類型的數據進行排序,如數組。
sorted(by:)的作用:對已知類型的數組進行排序。
sorted(by:)的參數:(String, String) -> Bool。
下面對字元串數組進行排序,以說明閉包的幾種應用形式
***/

/** 1. 使用函數 **/
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
let a = names.sorted(by:backward)
print("1. ", a)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

/** 2. 使用閉包,閉包類型與參數類型相同 **/
let b = names.sorted(by:{(s1: String, s2: String) -> Bool in return s1 > s2})
print("2. ", b)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

/** 3. 使用閉包,可從上下文推斷其參數與返回數據的類型,所以可優化為 **/
let c = names.sorted(by:{s1, s2 in return s1 > s2})
print("3. ", c)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

/** 4. 使用閉包,因閉包只含單條語句,則可省掉「return」,所以可優化為 **/
let d = names.sorted(by:{s1, s2 in s1 > s2})
print("4. ", d)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

/** 5. 使用閉包,Swift自動為閉包提供$0,$1,$2之類,表示傳入的參數,所以可以優化為 **/
let e = names.sorted(by:{$0 > $1})
print("5. ", e)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

/** 6. 使用閉包,用這種更短的方式,是基於大於號在木Swift中的定義, **/
/** 它的定義與sorted(by:)的參數類型相同,即比較兩個字元串並返回布爾值 **/
let f = names.sorted(by: > )
print("6. ", f)
// Prints ["Ewa", "Daniella", "Chris", "Barry", "Alex"]

尾隨閉包:一種語法糖。

import Cocoa // 使用arc4random(),需要引入Cocoa

/** 1. 定義一個使用閉包的函數 **/
func pointOfLayout(point:(_ x:UInt32, _ y:UInt32)->String) {
let x = arc4random() % 1280
let y = arc4random() % 960
let a = point(x, y)
print(a)
}

/** 2. 常規調用閉包,參考上節第5點 **/
pointOfLayout(point: {"point.x = ($0), point.y = ($1)"})
// Prints point.x = 1181, point.y = 382

/** 3. 尾隨調用諸包,一種語法糖形式 **/
pointOfLayout{"point.x = ($0), point.y = ($1)"}
// Prints point.x = 1101, point.y = 531

/** 4. 當閉包內含多行語句時,尾隨閉包更具有可讀性 **/
pointOfLayout{(_ x:UInt32, _ y:UInt32)->String in
let a = x * 100
let b = x * 100
return "point.x = (a), point.y = (b)"
}
// point.x = 89200, point.y = 89200

閉包轉義:閉包在函數內未調用,則需要聲明轉義 ,使用 @escaping。

var closures: [() -> Void] = []
/** 閉包沒有在函數里調用,只是保存在數組裡,所以需要轉義 **/
func notCalledClosure(closure: @escaping () -> Void) {
closures.append(closure)
}

/** 閉包在函數里被調用,不需要轉義 **/
func calledClosure(closure: () -> Void) {
closure()
}

/** 沒有輸出 **/
notCalledClosure { print(100) } // No output

/** 立即輸出 **/
calledClosure { print(200) } // Prints 200

下一篇,Swift 的進階。

讓我們在這裡,遇見明天的自己!姜友華


推薦閱讀:
相关文章