学习之前你要有所准备,它不仅会花费你的一些时间,也许还会花费你的一些钱:

首先:你需要一个苹果电脑,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 的进阶。

让我们在这里,遇见明天的自己!姜友华


推荐阅读:
相关文章