1.2、OC->Swift:函数与闭包

2022-11-06  本文已影响0人  双鱼子曰1987

零、概述


一、函数基础

1、声明与定义

func sayHelloWorld() -> String {
    print("hello, world")
}

sayHelloWorld()
// 打印“hello, world”

2、函数参数

2.1、参数列表:(参数名1:类型, 参数名2:类型, 参数名3:类型)

2.2、参数标签(即参数别名,和OC类似)

a、默认支持参数同名标签
func testFunc(name: String) {
}
testFunc(name: "haha")
b、支持自定义函数的参数标签
func testFunc_0(name: String) {
}
testFunc_0(name: "haha")

func testFunc_1(nickname name: String) {
}
testFunc_1(nickname: "123")
c、忽略参数标签:使用一个下划线_来代替一个参数标签;调用的时候忽略标签名。
func someFunction(_ firstParameterName: Int, secondParameterName: Int) { 
    ... 
}
someFunction(10)

2.3、支持函数默认值:(参数名1:类型, 参数名2:类型 = 默认值)

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    ...
}

someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) 
// 调用函数时,可以不传默认参数
someFunction(parameterWithoutDefault: 4)

2.4、可变参数:(参数名:类型 ...)

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    // numbers为double的数组
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)
// 返回 3.0, 是这 5 个数的平均数。
arithmeticMean(3, 8.25, 18.75)
// 返回 10.0, 是这 3 个数的平均数。

复合参数:默认值 + 可变参数

func testFunc(_ name:String, age:Int=0, list:[String]) {
    print("name:\(name), age:\(age), list:\(list)")
}

testFunc("hui", list: ["a", "b", "c"])
testFunc("hui", age: 18, list: ["a", "b", "c"])

2.5、输入输出参数(类似于,C语言的指针语法)

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

var someInt = 3
var anotherInt = 107
// 3,107
print("\(someInt),\(anotherInt)")

swapTwoInts(&someInt, &anotherInt)
// 107,3
print("\(someInt),\(anotherInt)")

3、返回值

func sayHello() -> Void {
    print("Hello World!");
}

// 等价于
func sayHello() {
    print("Hello World!");
}
// 返回数组中的最大值和最小值
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)
}

let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// 打印“min is -6 and max is 109”

返回可选元组:如果函数返回的元组类型有可能整个元组都“没有值”。

func minMax(array: [Int]) -> (min: Int, max: Int)? {
    ...
    return (currentMin, currentMax)
}
func greeting(for person: String) -> String {
    // 没有return,默认返回字符串
    "Hello, " + person + "!"
}
print(greeting(for: "Dave"))
// 打印 "Hello, Dave!"

二、函数类型

在Swift中,函数的一等公民,可以当做类型、函数参数、返回值等,支持面向函数编程。

1、定义与语法

//  函数类型:(Int, Int) -> Int
func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
// 1、函数类型,做为变量
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))") // Prints "Result: 5"


// 2、函数类型,做为函数参数
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5) // 打印“Result: 8”

// 3、函数类型,作为返回类型
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero 现在指向 stepBackward() 函数。

2、函数嵌套

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}

var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!

三、闭包

Swift的闭包类似于OC的(block) 或者 其他语言的匿名函数(lambdas)语法,其目的都是提供简易便捷的代码块封装方式。

1、闭包的语法格式

下面将以Swift 标准库提供了名为 sorted(by:) 的方法做为闭包的讲解。

a、标准语法:

大括号{}包裹:标识整个闭包代码块;
函数类型声明:括号()内定义参数列表;箭头符号-> 闭包返回值类型;
关键字in:标识 闭包 代码语句的开始

{ (parametersList) -> returnType in
    // statements
}

例子:以集合类型的系统排序函数sorted为例

names.sorted(by: { (s1: String, s2: String) -> Bool in
    return s1 > s2
})

// 等价于
names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )

b、闭包 支持的 参数和返回值

c、闭包语法糖:闭包的各种省略模式


2、闭包的值捕获 和 循环引用

2.1、捕获

func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}
return incrementer
}

// 由于 闭包或者嵌套函数 addByTen 和 addBySix 各自分别捕获一份变量 runningTotal,
// 因此,addByTen 和 addBySix 调用不会相互干扰
let addByTen = makeIncrementer(forIncrement: 10)
let addBySix = makeIncrementer(forIncrement: 6)

print(addByTen()) // 10
print(addByTen()) // 20

print(addBySix()) // 6
print(addBySix()) // 12

print(addByTen()) // 30

2.2、闭包的循环强引用

2.3、循环强引用的解决:捕获列表

// 1、如果闭包有参数列表和返回类型,把捕获列表放在它们前面:
lazy var someClosure: (Int, String) -> String = {
    [unowned self, weak delegate = self.delegate!] (index: Int, stringToProcess: String) -> String in
    // 这里是闭包的函数体
}
// 2、如果闭包没有指明参数列表或者返回类型,它们会通过上下文推断,那么可以把捕获列表和关键字 in 放在闭包最开始的地方:
lazy var someClosure: () -> String = {
    [unowned self, weak delegate = self.delegate!] in
    // 这里是闭包的函数体
}

2.4、弱引用weak 和 无主引用unowned的区别


3、逃逸闭包,关键字@escaping

3.1、逃逸闭包 的 定义:

一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。

/* 
* 函数接受一个 闭包completionHandler 作为参数,该闭包被添加到一个函数外定义的数组中。
* 如果你不将这个参数标记为 @escaping,就会得到一个编译错误。
*/
var completionHandlersList: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
    completionHandlersList.append(completionHandler)
}

3.2、类方法 与 逃逸闭包

如果将一个闭包标记为 @escaping 意味着,必须在闭包中显式地引用 self

func someFunctionWithNonescapingClosure(closure: () -> Void) {
    closure()
}

class SomeClass {
    var x = 10
    func doSomething() {
        /* 1、逃逸闭包 */
        someFunctionWithEscapingClosure { 
            self.x = 100  // 逃逸闭包,必须显示的引用 self
        }
      
        /* 2、非逃逸闭包 */
        someFunctionWithNonescapingClosure { 
            x = 200 // 非逃逸闭包,可以隐式的引用 self
        } 
    }
}

3.3、为什么要分逃逸闭包和非逃逸闭包?


4、语法糖:尾随闭包

函数调用时,针对最后一个函数参数是比闭包情况 的省略模式。

func someFunctionThatTakesAClosure(closure: () -> Void) {
    ...
}

// 以下是不使用尾随闭包进行函数调用
someFunctionThatTakesAClosure(closure: {
    ...
})

// 以下是使用尾随闭包进行函数调用
someFunctionThatTakesAClosure() {
    ...
}
names.sorted() { $0 > $1 }
// 等价于
names.sorted { $0 > $1 }

5、语法糖:自动闭包,关键字@autoclosure

// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
// 参数 customerProvider 的类型:函数类型() -> String,
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}
serve(customer: { customersInLine.remove(at: 0) } )
// 打印出“Now serving Alex!”



// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}

// 自动闭包:省略闭包的花括号`{}`,用一个普通的表达式来代替显式的闭包。
serve(customer: customersInLine.remove(at: 0))
// 打印“Now serving Ewa!”

四、函数和闭包都是引用类型

无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用。

上一篇下一篇

猜你喜欢

热点阅读