【学习】重学Swift5-函数和闭包
五、函数和闭包
函数
-
常见形式
// 无形式参数的函数 func sayHelloWorld() -> String { return "hello world" } print(sayHelloWorld()) // 多形式参数的函数 func greet(person: String, alreadyGreeted: Bool) -> String { if alreadyGreeted { return greetAgain(person: person) } else { return greet(person: person) } } greet(person: "tim", alreadyGreeted: true) // 无返回值的函数 func greet(person: String) { print("hello, \(person)!") } // 多返回值的函数 func minMax(array: [Int]) -> (min: Int, max: Int) { var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } // 可选元组返回类型 func minMax(array: [Int]) -> (min: Int, max: Int)? { if array.isEmpty {return nil} var currentMin = array[0] var currentMax = array[0] for value in array[1..<array.count] { if value < currentMin { currentMin = value } else if value > currentMax { currentMax = value } } return (currentMin, currentMax) } // 隐式返回的函数 // 如果整个函数体是一个单一表达式,那么函数可以隐式返回这个表达式 func greeting(person: String) -> String { "hello " + person + "!" } print(greeting(person: "Tom"))
-
实参标签、形参名和返回值
/* 每一个函数的形式参数都包含实际参数标签和形式参数名。实际参数标签用在调用函数的时候;在调用函数的时候每一个实际参数前面都要写实际参数标签。形式参数名用在函数的实现当中。默认情况下,形式参数使用它们的形式参数名作为实际参数标签。 所有的形式参数必须有唯一的名字。尽管有可能多个形式参数拥有相同的实际参数标签,唯一的实际参数标签有助于让你的代码更加易读 */ func someFunction(firstParameterName: Int, secondParameterName: Int) { } someFunction(firstParameterName: 1, secondParameterName: 3) 上面的🌰中没有定义实际参数标签,所以使用形式参数(firstParameterName、secondParameterName)名作为实参标签。在函数体内部使用形参实现逻辑。 // 指定实际参数标签 /* 在提供形式参数名之前写实际参数标签,用逗号分隔 如果你为一个形式参数提供了实际参数标签,那么这个实际参数就必须在调用函数的时候使用标签 实际参数标签的使用能够让函数的调用更加明确,更像是自然语句,同时还能提供更可读的函数体并更清晰的表达你的意图 */ func greet(person: String, frome hometown: String) -> String { return "hello \(person)! Glad you coule visit frome \(hometown)" } print(greet(person: "Bill", frome: "Cupertino")) 上面的🌰中第一个参数没有定义实际参数标签,所以使用形式参数(person)名作为实参标签。第二个参数指定了(from)作为实际参数标签,(hometown)作为形式参数名。所以在调用的时候显示了(from),在内部使用(hometown) // 省略实际参数标签 使用_替代 func someFunction(_ firstParameterName: Int, secondParameterName: Int) { } someFunction(1, secondParameterName: 3) 上面的🌰中第一个使用_来省略实际参数标签 // 默认形式参数值 func someFunction(parameterWithDefault: Int = 12) { print(parameterWithDefault) } someFunction(parameterWithDefault: 44) someFunction() // 可变形式参数 func arithmeticMean(_ numbers: Double...) -> Double { var total: Double = 0 for num in numbers { total += num } return total } arithmeticMean(1, 2, 3) arithmeticMean(1) // 输入输出形式参数 /* 可变形式参数只能在函数的内部做改变。如果想函数能够修改一个形式参数的值,而且你想这些改变在函数结束后依然生效,就需要将形式参数定义为输入输出形式参数。 在形式参数定义开始的时候在前面添加一个inout关键字可以定义一个输入输出形式参数。 只能把变量作为输入输出形式参数的实际参数,再将变量作为实际参数传递给输入输出形式参数的时候,直接在它的前面添加一个&来明确可以被函数修改 输入输出形式参数不能有默认值,可变形式参数不能标记为inout */ func swapTwoInts(_ a:inout Int, _ b:inout Int) { let temp = a a = b b = temp } var a = 3 var b = 4; swapTwoInts(&a, &b) print(a, b)
-
函数类型和内嵌函数
// 函数类型由形式参数类型,返回类型组成 func addTwoInts(_ a: Int, _ b: Int) -> Int { return a + b } func muliplyTwoInts(_ a: Int, _ b: Int) -> Int { return a * b } 上面两个🌰都是(Int, Int) -> Int 类型 // 使用函数类型 var mathFunction: (Int, Int) -> Int = addTwoInts(_:_:) print(mathFunction(2, 4)) // 作为参数 func printMathResult(_ mathFunction:(Int, Int) -> Int, _ a: Int, _ b: Int) { print("result:\(mathFunction(a,b))") } printMathResult(addTwoInts(_:_:), 3, 4) // 作为返回值 func stepForward(_ input: Int) -> Int { return input + 1 } func stepBackward(_ input: Int) -> Int { return input - 1 } func chooseStepFunction(backwards: Bool) -> (Int) -> Int { return backwards ? stepBackward(_:) : stepForward(_:) } // 内嵌函数 func chooseStepFunction(backwards: Bool) -> (Int) -> Int { func stepForward(_ input: Int) -> Int { return input + 1 } func stepBackward(_ input: Int) -> Int { return input - 1 } return backwards ? stepBackward(_:) : stepForward(_:) }
闭包
-
定义
/* 闭包是可以在你的代码中被传递和引用的功能性独立代码块 闭包能够捕获和存储定义在其上下文中的任何常量和变量的引用。 */ /* 闭包符合如下三种形式中的一种: 1.全局函数是一个有名字但不会捕获任何值的闭包; 2.内嵌函数是一个有名字且能从其上层函数捕获值的闭包; 3.闭包表达式是一个轻量级语法所写的可以捕获其上下文中常量或变量值的没有名字的闭包 */ // 闭包表达式是一种在简短行内就能写完闭包的语法。 // 闭包表达式语法能够使用常量形式参数、变量形式参数和输入输出形式参数,但不能提供默认值。可变形式参数也能使用,但是需要在形式参数列表的最后使用,元组也可被用来作为形式参数和返回类型 // 结构如下 { (parameters) -> (return type) in statements } // 不使用闭包 let names = ["zhangsan", "lisi", "wangwu", "maliu"] let fNames = names.sorted() func backward(s1: String, s2: String) -> Bool { return s1 > s2 } let sNames = names.sorted(by: backward(s1:s2:)) // 使用闭包 let sNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 }) let sName = names.sorted(by:{ s1, s2 in return s1 > s2 } ) // 单表达式闭包能通过从它们的声明中删除return关键字来隐式返回单个表达式的结果 let sName = names.sorted(by:{ s1, s2 in s1 > s2 } ) // swift自动对行内闭包提供间歇实际参数名,可以通过$0,$1,$2等名字来引用闭包的实际参数值 let sName = names.sorted(by:{ $0 > $1 } ) // 运算符函数 /* swift的String类型定义了关于大于号(>)的特定字符串实现,其类型正好与sorted(by:)所需形参一致,所以可以更简单的书写 */ let sName = names.sorted(by: > ) // 尾随闭包 /* 尾随闭包是一个被书写在函数形式参数的括号外面(后面)的闭包表达式 条件:只有一个参数,且是一个闭包,可以直接加闭包,并且去掉括号 */ let sName = names.sorted{$0 > $1 }
-
闭包捕获值
/* 一个闭包能够从上下文捕获已被定义的常量和变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍能够在其函数体内引用和修改这些值。 在swift中,函数和闭包都是引用类型。 如果你分配了一个闭包给类实例的属性,并且闭包通过引用该实例或者它的成员来捕获实例,你将在闭包和实例间会产生循环引用 */
-
逃逸闭包、自动闭包
// 逃逸闭包 // 自动闭包