Swift语法基础入门三(函数, 闭包)
函数:
- 函数是用来完成特定任务的独立的代码块。你给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这个名字会被用于“调用”函数
- 格式:
- func 函数名称(参数名:参数类型, 参数名:参数类型...) -> 函数返回值 { 函数实现部分 }
没有参数没有返回值
- 可以写为 ->Void
- 可以写为 ->()
- 可以省略
- Void。它其实是一个空的元组(tuple),没有任何元素,可以写成()
func say() -> Void {
print("hello")
}
say()
func say1() -> () {
print("hello")
}
say1()
// 推荐
func say2() {
print("hello")
}
say2()
有参数没有返回值
内部/外部参数
- 内部参数: Swift2.0以前, 默认情况下的参数都是内部参数
- Swift2.0开始, 默认将第二个参数名称作为外部参数
- 如果没有明确地指定外部参数, 那么系统默认会从第二个参数开始, 将参数的名称作为外部参数
- 外部参数只能外部用, 函数内部不能使用, 函数内部只能使用内部参数
- 忽略外部参数: 在内部参数前加_
// Swift2.0之前, 默认是不会将第二个参数开始的参数名称作为外部参数的, 必须自己手动指定
func sum(i: Int, j: Int) {
print(i + j)
}
sum(10, j: 20)
func sum1(first i: Int, second j: Int) {
print(i + j)
}
sum1(first: 10, second: 20)
默认参数(Default Parameter Values)
- 格式: func method(parameter: Int = 0){}
- 当默认值被定义后,调用这个函数时可以忽略这个参数
- 其它语言的默认参数必须写在最后面, Swift可以写在任意位置
注意
- 将带有默认值的参数放在函数参数列表的最后。这样可以保证在函数调用时,非默认参数的顺序是一致的,同时使得相同的函数在不同情况下调用时显得更为清晰。
func sum2(i: Int, j: Int = 10) {
print(i + j)
}
//sum2(10, j: 20)
sum2(10)
// 不推荐这样写, 最好将默认参数写在最后
func sum3(i: Int = 20, j: Int) {
print(i + j)
}
常量参数和变量参数(Constant and Variable Parameters)
- 函数参数默认是常量, 在函数内部不能修改
- 如果想在函数中修改参数, 必须在参数前加上var
注意
- 对变量参数所进行的修改在函数调用结束后便消失了,并且对于函数体外是不可见的。变量参数仅仅存在于函数调用的生命周期中
func sum4(let i: Int, let j: Int) {
print(i + j)
}
sum4(10, j: 20)
var num1 = 10
var num2 = 20
//func swap(value1: Int, value2: Int){
// let temp = value1
// value1 = value2
// value2 = temp
//}
// 注意: 操作的是局部变量, 对实参没有影响
func swap1(var value1: Int, var value2: Int){
print("交互前: value1 = (value1), value2 = (value2)")
let temp = value1
value1 = value2
value2 = temp
print("交互后: value1 = (value1), value2 = (value2)")
}
swap1(num1, value2: num2)
print(num1)
print(num2)
输入输出参数(In-Out Parameters)
- 变量参数,正如上面所述,仅仅能在函数体内被更改。如果你想要一个函数可以修改参数的值,并且想要在这些修改在函数调用结束后仍然存在,那么就应该把这个参数定义为输入输出参数(In-Out Parameters)
- 定义一个输入输出参数时,在参数定义前加 inout 关键字
注意
- 输入输出参数不能有默认值,而且可变参数不能用 inout 标记。如果你用 inout 标记一个参数,这个参数不能被 var 或者 let 标记。
func swap2(inout value1: Int, inout value2: Int){
print("交互前: value1 = (value1), value2 = (value2)")
let temp = value1
value1 = value2
value2 = temp
print("交互后: value1 = (value1), value2 = (value2)")
}
swap2(&num1, value2: &num2)
print(num1)
print(num2)
可变参数(Variadic Parameters)
- 一个可变参数可以接收零个或多个值
- 如果没有变参函数 , 并且函数的参数个数又不确定那么只能写多个方法或者用将函数参数改为集合
- 格式 func method(parameter: Int...){}
- 可变参数在函数中可以当做一个数组
注意
- 一个函数最多只能有一个可变参数
- 变参只能是同种类型的数据
- 变参必须指定数据类型
- 如果函数有一个或多个带默认值的参数,而且还有一个可变参数,那么把可变参数放在参数表的最后
func sum5(numbers: Int...) {
// print(numbers)
var sum = 0
for number in numbers {
sum += number
}
print(sum)
}
sum5(1, 2, 3)
// 不推荐写法, 和默认值一样, 变参最好写在最后
func sum6(numbers: Int..., var sum: Int) {
// print(numbers)
for number in numbers {
sum += number
}
print(sum)
}
sum6(1, 2, 3, sum: 0)
// 推荐写法
func sum7(var sum: Int = 100, numbers: Int...) {
// print(numbers)
for number in numbers {
sum += number
}
print(sum)
}
sum7(numbers: 1, 2, 3)
// 一个函数中只能有一个变参
//func sum8(numbers: Int..., values: Int...){
// print(numbers)
// print(values)
//}
// 有参数有返回值
func sum8(i: Int, j: Int) -> Int {
return i + j
}
let result = sum8(10, j: 20)
print(result)
闭包
闭包
- 闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似
- 闭包可以捕获和存储其所在上下文中任意常量和变量的引用。这就是所谓的闭合并包裹着这些常量和变量,俗称闭包
- 闭包的使用和block一样, 用于保存一段代码, 用作回调, 用作执行耗时操作
- 闭包格式: in关键字的目的是便于区分返回值和执行语句
{ (形参列表) -> 返回值类型 in 执行语句 }
// 正常写法
loadData ({ () -> () in
print("执行了")
})
*/
/*
// 闭包的其它写法
// 1.如果闭包是函数的最后一个参数, 那么可以把闭包写在调用函数的()后面
// 这种写法, 我们称之为 "尾随闭包"
loadData("123") {
() -> ()
in
print("执行了")
}
// 2.如果函数只接收一个参数, 并且这个参数是闭包, 那么调用函数的()可以省略
// 这种写法, 我们称之为 "尾随闭包"
loadData {
() -> ()
in
print("执行了")
}
func loadData(since_id: String, finished: ()->()) -> Void {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("子线程做耗时操作 (NSThread.currentThread())")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("主线程更新UI (NSThread.currentThread())")
finished()
})
}
}
/*
func loadData(finished: ()->()) -> Void {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("子线程做耗时操作 (NSThread.currentThread())")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("主线程更新UI (NSThread.currentThread())")
finished()
})
}
}
闭包的循环引用
- 闭包循环强引用
-
block
- 闭包和block很像, 都是提前准备好代码, 在需要时执行
- block会对外部变量进行强引用, 保证执行代码时变量还在
- block中用到self一定要非常小心 闭包
- 闭包也一样, 会对外部变量进行强引用, 保证执行代码时变量还在
- 如果您将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,您将创建一个在闭包和该实例间的循环强引用
- Swift开发中能不写self就不写self, 一看到self就想到闭包
-
OC中如何解决循环引用
- __weak typeof(self) weakSelf = self;
- 特点: 对象释放后会自动将变量赋值为nil
- __unsafe_unretained typeof(self) weakSelf = self;
- 特点: 对象释放后不会自动将变量赋值为nil, 指向一块废弃的存储空间
-
Swift中如何解决循环引用
- weak var weakSelf = self
- weak 相当于OC中的 __weak, 和OC一样 对象释放后会自动将变量赋值为nil
-
所以被weak修饰的变量是可选类型
-
unowned var weakSelf = self
- unowned 相当于OC中的__unsafe_unretained, 和OC一样, 对象释放后不会自动将变量赋值为nil
-
所以被unowned修饰的变量, 不是可选类型
注意: weak和unowned只能修饰对象类型, 因为只有对象类型才有引用计数
-
应用场景:
- 什么时候用weak
- 当被保存的对象有可能提前释放时就用weak
- 什么时候用unowned
- 当被保存的对象在使用时不会被提前释放时就用unowned
- 什么时候用weak
-
捕获列表
- 可以在
调用
闭包时在形参列表前面通过[]指定捕获列表, 告诉系统如何处理指定的这些值
- 可以在
//weak var weakSelf = self
//unowned var weakSelf = self
callBack = { [unowned self ,weak btn = self.button] () -> () in
//self.view.backgroundColor = UIColor.redColor()
//weakSelf.view.backgroundColor = UIColor.redColor()
self.view.backgroundColor = UIColor.redColor()
print(btn)
}
loadData(callBack!)
}
func loadData(finished: ()->()) -> Void {
dispatch_async(dispatch_get_global_queue(0, 0)) { () -> Void in
print("子线程做耗时操作 (NSThread.currentThread())")
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("主线程更新UI (NSThread.currentThread())")
finished()
})
}
}
- 析构函数
- 析构器只适用于类类型,当一个类的实例被释放之前,析构器会被立即调用
- 类似于OC中的dealloc方法
- 析构器是在实例释放发生前被自动调用。你不能主动调用析构器
- 一般情况下, 当使用自己的资源时, 在析构函数中进行一些额外的清理
例如,如果创建了一个自定义的类来打开一个文件,并写入一些数据,你可能需要在类实例被释放之前手动去关闭该文件
deinit {
print("88")
}