用Swift開發macOS程序, 二、Swift 的基礎
學習之前你要有所準備,它不僅會花費你的一些時間,也許還會花費你的一些錢:
首先:你需要一個蘋果電腦,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點。
本章講的是函數編程部分。枚舉、結構體、類下一章再講。
- 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
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 ["大冶": "銅都", "昆明": "春城"]
七、控制語句:控制語分循環語句、條件語句。
- 循環語句:for-in,while/repeat-while。
- 條件語句:if/if-else/if-else if-else,switch。
- 關鍵詞: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
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
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
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 的進階。
讓我們在這裡,遇見明天的自己!姜友華
推薦閱讀: