我的博客地址: fengqiangboy.com
文档更新说明:
• 2016年10月12日 v1.0 初稿
• 2016年10月13日 v2.0 增加闭包部分
1、函数
1.1、函数的定义和调用
- 使用
func
关键字来定义一个函数 - 在swift中,大概我们也可以自豪的说"一切皆对象"了,函数也可以当做一个特殊的对象来看待。
// 定义一个不带参数的函数
func sendMessage() {
let message = "Hey there!"
print(message)
}
// 调用不带参数的函数
sendMessage()
/**
输出结果:
Hey there!
*/
1.2、带参数的函数
- 定义带参数的函数,只需要在定义的时候,括号内填入相应的参数,默认情况下,是用
let
声明的常量 - 在下面的定义中,
func
代表定义一个函数,是个关键字,sendMessage
是这个函数的名字,shouting
是参数的名字,可在函数内部使用,在调用的时候需要声明写出来Bool
代表参数类型,这里是一个布尔类型
// 定义一个带参数的函数
func sendMessage(shouting: Bool) {
var message = "Hey there!"
if shouting {
message = message.uppercased()
}
print(message)
}
// 调用一个带参数的函数
sendMessage(shouting: true)
/**
输出结果:
HEY THERE!
*/
1.3、函数的参数标签
- swift中,为了使函数在调用和定义的时候都能见名知意,提供了两个命名(参数标签和参数名),中间以空格分开
- 参数名是给调用的时候函数内部使用的,参数标签是调用函数的时候,当做函数名来用的
- 实际使用中,合理运用这两个参数,可以大大提高代码的可读性
- 一个函数中,多个参数名不能重名,但是参数标签可以重名(建议不要那么无聊去写重名的)
- 下面函数中“
to
”就是函数标签,recipient
和shouting
是参数名 - 不想在调用的时候写出参数标签,可以在定义函数的时候使用“
_
”来代替参数标签,(1.4)
func sendMessage(to recipient: String, shouting: Bool) {
var message = "Hey there, \(recipient)!"
if shouting {
message = message.uppercased()
}
print(message)
}
sendMessage(to: "Morgan", shouting: false)
/**
输出结果:
Hey there, Morgan!
*/
1.4、省略参数标签
- 有些参数名可能会跟函数名重名,比如下面这个函数,两个message,在调用的时候就会显得特别奇怪,这时,可以用"
_
"来代替参数名字,调用的时候看起来就更加舒服了
// 名字优化之前
func sendMessage(message: String, to recipient: String, shouting: Bool)
// 省略参数名字之后
func sendMessage(_ message: String, to recipient: String, shouting: Bool) {
var message = "\(message), \(recipient)!"
if shouting {
message = message.uppercased()
}
print(message)
}
// 调用的时候可以省略参数名字
sendMessage("See you at the Bash", to: "Morgan", shouting: false)
/**
输出结果:
See you at the Bash, Morgan!
*/
1.5、参数的默认值
- 在上面的函数中,其实最后那个参数我们一般都是传false,这时,调用的时候再每次都写这个参数就显得多余了
- swift中提供了一个默认参数,即可以给函数的参数一个默认值,调用的时候不传就使用默认值
- 建议把带默认值的参数写在后面,否则,你会回来点赞的(调用的时候参数顺序看得清晰些)
- 默认参数在定义函数的时候写在类型后面,如下:
// 参数带有默认值的函数
func sendMessage(_ message: String, to recipient: String, shouting: Bool = false) {
var message = "\(message), \(recipient)!"
if shouting {
message = message.uppercased()
}
print(message)
}
// 调用带有默认值的函数
sendMessage("See you at the Bash", to: "Morgan")
/**
输出结果:
See you at the Bash, Morgan!
*/
1.6、可变参数
- 可变参数指的是:这个参数可以传入0个,也可以传入10000个,就像OC里面的
NSLog()
函数一样,后面参数个数的不定的 - 可变参数的定义:在定义参数类型的后面加上省略号
...
,比如func someFunction(argumentLabel parameterName: Int...)
,parameterName
参数就是个可变参数了 - 可变参数的获取:在函数内部,可变参数以数组的形式体现的,上面定义的那个可变参数的获取方式
parameterName[0]
,这样,就拿到了可变参数的第一个值
// 可变参数
func optParameter(optionPara: Int...) {
for num in optionPara {
print(num)
}
}
// 调用可变参数函数
optParameter(optionPara: 1, 2, 3, 4, 5)
/**
输出结果:
1
2
3
4
5
*/
1.7、输入输出参数(C语言中的指针参数)
- 在C语言中,我们可以给函数传入一个指针值,以达到在函数内部修改外部参数值的目的,swift中,这种参数叫做
输入输出
参数 - 定义一个输入输出参数,只需要在参数类型前面加一个
inout
- 调用的时候,需要在输入输出参数前面加"
&
" - 调用的时候,输入输出参数必须传入变量,不能是常量
- 输入输出参数不能有默认值
// 定义一个交换两数的方法
func swap(a:inout Int, b:inout Int) {
let c = a
a = b
b = c
}
var a = 1
var b = 2
print("before: a=\(a), b=\(b)")
// 调用交换方法
swap(&a, &b)
print("after: a=\(a), b=\(b)")
/**
输出结果:
before: a=1, b=2
after: a=2, b=1
*/
1.8、函数类型
- 回到开头,我说过,在swift中,我们也可以称为“一切皆对象”,那么,函数既然可以称之为对象,肯定有类型
- 函数的类型:有个技巧,不管是什么类型,把参数名变量名全部删掉,剩下的就是类型了。上面的那个函数,类型就应该是
(inout Int, inout Int) -> Void
,解读为:这个函数类型接受两个输入输出的Int类型参数,并返回Void
- 函数类型的使用:同其他类型一样的,上面那个函数类型就可以这样使用
var function: (inout Int, inout Int) -> Void = swap
- 同样,函数类型也可以作为函数的参数和返回值
函数作为参数的例子
// 这个函数用来作为参数
func sum(num1: Int, num2: Int) -> Int {
return num1+num2
}
// 这个函数接受一个(Int, Int)->Int类型的函数作为参数
func printResault(num1: Int, num2: Int, ruler:(Int, Int)->Int) {
// 调用传进来的函数
print(ruler(num1, num2))
}
// 调用函数,并把参数传进去
printResault(num1: 4, num2: 5, ruler: sum)
/**
输出结果:
9
*/
函数作为返回值
// 减1
func stepForward(_ input: Int) -> Int {
return input + 1
}
// 加1
func stepBackward(_ input: Int) -> Int {
return input - 1
}
// 根据传入的backward值,返回加1函数或者减1函数
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 现在指向 stepBackward() 函数。
//调用返回的函数
print(moveNearerToZero(currentValue))
/**
输出结果:
2
*/
1.9、嵌套函数
-
嵌套函数
就是在函数里面在定义一个函数,相对的,前面说的那些都是全局函数
-
嵌套函数
只能在内部使用
2、闭包
2.1、什么是闭包
- 闭包 就是“闭合包裹常量和变量的代码块”的简称,写过OC的人对OC中的 Block 应该不会陌生,其实闭包就是swift里面的“Block”
- 闭包是引用类型
- 闭包的定义和函数很像,把函数的各种名称部分去掉,在用花括号括起来,其实就是一个闭包了,闭包的一般定义形式如下:
{(params) -> returnType in
statemems
}
-
in
关键字表示闭包的参数和返回值类型定义完成,闭包函数体开始 - 闭包参数可以是
inout
类型,但是,不能有默认参数 - 闭包完整格式实例
let grade = [90, 100, 80, 76, 88, 65, 65, 63]
print("before \(grade)")
let grade_sort = grade.sorted (by: { (num1: Int, num2: Int) -> Bool in
return num1 > num2
})
print("after \(grade_sort)")
/**
输出结果:
before [90, 100, 80, 76, 88, 65, 65, 63]
after [100, 90, 88, 80, 76, 65, 65, 63]
*/
2.2闭包格式大简化
- 函数参数为闭包的时候,编译器可以自动推断出闭包的类型的,比如说在下面的代码中,闭包的类型就可以省略,直接在括号内写参数名
- 闭包作为函数最后一个参数的时候,可以省略函数调用的小括号,直接把闭包跟在函数名后面,同时,参数标签也可以省略掉,这个闭包又称做
尾随闭包
,经过这两项优化之后,上面的代码变成了下面这样
let grade = [90, 100, 80, 76, 88, 65, 65, 63]
print("before \(grade)")
let grade_sort = grade.sorted { (num1, num2) in
return num1 > num2
}
print("after \(grade_sort)")
/**
输出结果:
before [90, 100, 80, 76, 88, 65, 65, 63]
after [100, 90, 88, 80, 76, 65, 65, 63]
*/
- 如果闭包中,只有一句代码,那么闭包会自动把这句代码的结果当做返回值处理,也就是说,上面的闭包我们可以省略
return
关键字,省略之后,代码如下
let grade = [90, 100, 80, 76, 88, 65, 65, 63]
print("before \(grade)")
let grade_sort = grade.sorted { (num1, num2) in
num1 > num2
}
print("after \(grade_sort)")
/**
输出结果:
before [90, 100, 80, 76, 88, 65, 65, 63]
after [100, 90, 88, 80, 76, 65, 65, 63]
*/
- 在闭包中,参数可以使用缩写,用
$
表示,比如说$0
就是第一个参数,这时,前面的参数定义部分已经没有意义了,in
关键字也可以省略,代码如下
let grade = [90, 100, 80, 76, 88, 65, 65, 63]
print("before \(grade)")
let grade_sort = grade.sorted { $0 > $1 }
print("after \(grade_sort)")
/**
输出结果:
before [90, 100, 80, 76, 88, 65, 65, 63]
after [100, 90, 88, 80, 76, 65, 65, 63]
*/
2.3、逃逸闭包
-
逃逸闭包
指的是我们在函数内部接收了闭包之后,把闭包保存了起来,留着函数结束之后再调用这个闭包,这个闭包就叫做逃逸闭包 - 逃逸闭包需要在定义的类型前加
@escaping
,否则会编译报错
// 定义一个变量,用来存储一个闭包,给后面使用
var completeHandle:() -> Void = {}
// 定义一个函数,这个函数接受一个闭包,并且设置给外部变量,这里必须标记为 @escaping
func doSomeThing(completion: @escaping () -> Void) {
completeHandle = completion
}
print("设置之前")
// 调用函数,并设置一个闭包
doSomeThing {
print("完成了")
}
print("设置之后")
// 调用刚才设置好的闭包
completeHandle()
print("调用之后")
/**
输出结果:
设置之前
设置之后
完成了
调用之后
*/
2.4、闭包中的self处理
- 有过OC经验的童鞋应该都非常明白
Block
中不能强引用self
,否则会由于循环引用而导致内存泄露 - Swift中提供了更加简单的方法,在闭包中弱引用一个变量,只需要在闭包参数定义之前加入
[weak self]
,Swift就会自动将self
在闭包中弱引用 - 此时需要注意的是,弱引用可能不存在了,所以在这种情况下,
self
变成了一个可选值
{[weak self](params) -> returnType in
// self变成了可选值,需要用?来使用
self?.somePropety = ...
statemems
}
相关文章
1、Swift3入门教程之一基础部分
参考资料:
- Swift Standard Library Playground
- Swift.org
- The Swift Programming Language (Swift 3)
- Using Swift with Cocoa and Objective-C (Swift 3)
- WWDC2016:Session 404 Getting Started with Swift