由于 Swift 出自于名门 Apple,与 Objective-C 共同服务于 iOS/Mac OS X 平台,因而看得出它更像是脱胎于 Objective-C。现在来学习它的函数相关的知识,Swift 函数定义有些像 Scala,只是用 func 替代了 def, 返回类型的指定用的是 ->
而不是 =
, 格式如下:
1 2 3 4 5 |
func greet(name: String, day: String) -> String { return "Hello \(name), today is \(day)" } greet("Bob", day: "Tuesday") |
当然函数无参数就是空括号(不能省略空括号),无返回值的话就无需 ->
指定类型了,比如 func greet() { print("Hello")},从形式上看不出是否有副作用(纯函数)
调用函数时和 Objective-C 是一样的,除第一个参数不需要指定形参名,其他参数必须指定形参名,并且顺序是要和函数定义时完全一样的。我们知道在 Objective-C 中一般约定把第一次参数包含在方法名中,如
1 |
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle |
Scala 调用函数时指定形参名时,参数顺序是随意的,如
1 2 3 4 5 6 7 |
scala> def greet(name: String, day: String) = { | s"Hello $name, today is $day" | } greet: (name: String, day: String)String scala> greet(day="Tuesay", name="Bob") res2: String = Hello Bob, today is Tuesay |
Swift 的函数有返回值的话一定要用 ->
指定类型,并且函数中的 return
也是必不可以少的。而 Scala 的函数则用 =
来连接函数体,并能通过函数的的出口语句自动推断出返回类型,反而不能显示用 return
了; 指定了函数返回类型才能用 return.
外部参数名和本地参数名
和 Objective-C 类似,Swift 函数的参数也分为外部参数名和本地参数名。从外部调用函数时使用外部参数名,函数内部使用本地参数名来引用传入的值。只写一个参数名的话它即是外部也是本地参数名,第一个参数使用起来还比较特殊。
外部参数名和本地参数名全写
1 2 3 4 5 |
func someFunction(externalParameterName localParameterName: Int) { print(localParameterName) //函数内部只能用本地参数名来引用参数 } someFunction(externalParameterName: 100) //调用函数时可能使用外部参数名来传入参数 |
有两个参数时
1 2 3 4 5 |
func sayHello(to person: String, and anotherPerson: String) { print("Hello \(person) and \(anotherPerson)!") } sayHello(to: "Bill", and: "Ted") //这样即使是第一个参数也有了外部参数名 |
因为只有一个参数名的情况,外部和本地参数名都共用一个名字;再加之调用函数默认时第一个参数名要省略,所以我们可以只是第一个参数具备外部和本地参数名,其余只指定一个参数名,如此可使得调用函数时每个参数都带名称,像
1 2 3 4 5 |
func sayHello(to person: String, anotherPerson: String) { print("Hello \(person) and \(anotherPerson)!") } sayHello(to: "Bill", anotherPerson: "Ted") |
这种调用方式个人觉得比 sayHello("Bill", anotherPerson: "Ted") 或者 sayHelloTo("Bill", anotherPerson: "Ted") 更好更齐整。
通过 _
代替外部参数名可以让我们调用函数使用 Java 那样的语法 -- 无需指定外部参数名
1 2 3 4 5 |
func sayHello(to: String, _ anotherPerson: String) { print("Hello \(to) and \(anotherPerson)!") } sayHello("Bill", "Ted") |
函数返回多个值
Swift 也有 Tuple 类型,所以函数要返回多个值的话,就返回一个 Tuple, 如
1 2 3 4 5 6 7 8 |
func coordinate() -> (Double, Double) { return (42.023261, -87.801290) } coordinate().0 //42.023261 let (x, y) = coordinate() print(x |
Tuple 中元素个数随意,用基于 0 的下标进行访问;也能直接赋值给 Tuple 类型,也就同时获得各个值
Optional 参数和 Optional 返回类型
Swift 的参数或返回值可以支持 Optional 类型,还是老办法--像声明 变量一样,在类型后面加个问号,如
1 2 3 4 5 6 7 |
func coordinate(input: String?) -> (Double, Double)? { input.map{i in print(i)} //输出 Demo return (42.023261, -87.801290) } let input:String? = "Demo" //声明一个 Optional 的 String 类型 coordinate(input).map{value in print(value.0)} //得到的是一个 Optional 的 (Double, Double) 类型 |
可变个数参数(Variadic Parameters)
和 Objective-C(比如 nslog() 函数) 等众多语言一样 Swift 也支持可变个数参数(VarArgs)
1 2 3 4 5 |
func count(numbers: Int...) -> Int { return numbers.count } count() count(1,2) |
注意描述可变参数的格式是类型后面 ...
,numbers: Int...
,实际 numbers 类型也是一个数组。
回顾一下:在 Objective-C 中要实现一个可变参数函数麻烦些了,要这样做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (NSNumber *) addValues:(int) count, ... { va_list args; va_start(args, count); NSNumber *value; double retval; for( int i = 0; i < count; i++ ) { value = va_arg(args, NSNumber *); retval += [value doubleValue]; } va_end(args); return [NSNumber numberWithDouble:retval]; } |
而 Java 和 Scala 相对简单,参数形式分别是 (String... args) 和 (args: String*)。
函数默认参数
1 2 3 4 5 6 7 8 |
func foo(a1: String = "X", a2: String = "Y") -> String { //当然可以个别参数有默认值的 return a1 + a2 } foo() //XY foo("a") //aY foo("a", a2: "b") //ab foo(a2: "b") //Xb |
用外部参数名去匹配,第一个参数不写外部参数名。但如果写成下面的形式就有点傻眼了
1 2 3 |
func foo(a1: String = "X", _ a2: String) -> String { return a1 + a2 } //想要应用第一个默认参数,只传第二个参数时该如何? foo("a2") 是错的,又不能写成 foo(a2: "a2") |
var 函数参数
默认时函数的参数是一个常量,也就是说默认时把参数前的 let 省略了
1 2 3 4 5 6 7 |
func foo(number1: Int) { //它是 func foo(let number1: Int) 的简写 number1 = 1 //编译出错,提示 cannot assign to value: 'number1' is a 'let' constant } func bar(numbers: [Int]) { //它也是 func bar(let numbers: [Int]) 的简写形式 number1[0] = 10 //编译出误,提示 cannot assign through subscript: 'numbers' is a 'let' constant } |
看看 var 参数是什么样的状况
我们从输出发现,尽管 a, b 都是 var 变量,但它们经过函数调用后,它们的值或内部状态仍然保持不变,因为参数传入函数时产生了一个深拷贝。而 var 类型的函数参数只被借用来过渡一下。
Swift 对待参数的处理方式对于函数式编程意义重大,真正的 Immutable 了,并且实例是不被共享的。
inout 参数
既然上面的 let 和 var 都无法撼动传入参数的值,Swift 还有一绝招,inout 类型参数,像数据库存储过程一样,它所指定的参数引用或内部状态都可以改变。像是 C/C++ 的传地址
1 2 3 4 5 6 7 8 9 10 |
func inoutfunc(inout number1: Int, inout _ numbers: [Int]) { //inout 就相当于 C/C++ 的传地址 number1 = 10 numbers[0] = 20 } var a = 1 var b = [2,3] inoutfunc(&a, &b) //a, b 必须定义为 var,并且调用时必须用 & 取其地址 print(a) //输出10 print(b) //输出 [20, 3] |
参考: 1. Functions
本文链接 https://yanbin.blog/swift-learning-function/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。