用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_ 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 ["大冶": "铜都", "昆明": "春城"]
七、控制语句:控制语分循环语句、条件语句。
- 循环语句: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 aPointInt, 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(pointInt, 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 的进阶。
让我们在这里,遇见明天的自己!姜友华
推荐阅读: