Swift-尾随闭包、值捕获

尾随闭包

如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:例如

  • 无参数无返回值
func someFunctionThatTakesAClosure(closure: () -> Void) {
    // 函数体部分
}

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

// 以下是使用尾随闭包进行函数调用,一般情况下系统会自动识别尾随闭包
someFunctionThatTakesAClosure {
    // 闭包主体部分
}

当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的 Array 类型有一个 map(_:) 方法。

  • 数组的map(:)函数
    这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
    当提供给数组的闭包应用于每个数组元素后,map(
    :) 方法将返回一个新的数组,数组中包含了与原数组中的元素一一对应的映射后的值。
    下例介绍了如何在 map(_:) 方法中使用尾随闭包将 Int 类型数组 [16, 58, 510] 转换为包含对应 String 类型的值的数组["OneSix", "FiveEight", "FiveOneZero"]:
    创建两个数组,dagitNames是一个整型数位和它们英文版本名字相映射的字典。numbers是一个数字数组。
let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]

现在通过传递一个尾随闭包给 numbers 数组的 map(_:) 方法来创建对应的字符串版本数组:

//数字与字符串映射字典
        let digitNames = [
            0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
            5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
        ]
        //数字数组
        let numbers = [14, 15 ,35]
        //调用数组的map方法
        let stringArray = numbers.map { (number) -> String in
            var number = number
            var outpu = ""
            //循环处理数字的每一位
            repeat {
                //从 digitNames 字典中获取的字符串被添加到 output 的前部,逆序建立了一个字符串版本的数字。(在表达式  number % 10 中,如果 number 为 14,则返回 4,15 返回 5,350 返回 0。)
                
                //number 变量之后除以 10。因为其是整数,在计算过程中未除尽部分被忽略。因此 14 变成了 1,15 变成了  5,350 变成了 35。
                
                outpu = digitNames[number % 10]! + outpu
                //整个过程重复进行,直到 number /= 10 为 0,结束循环
                number /= 10
                
            } while number > 0
            return outpu;
        }
        
       print("stringArray=>\(stringArray)")
       //stringArray常量被推断为字符串类型数组,即 [String]
       //stringArray=>["OneFour", "OneFive", "ThreeFive"]

值捕获

闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。

Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。
举个例子,这有一个叫做 makeIncrementer 的函数,其包含了一个叫做 incrementer 的嵌套函数。嵌套函数 incrementer() 从上下文中捕获了两个值,runningTotalamount。捕获这些值之后,makeIncrementerincrementer 作为闭包返回。每次调用 incrementer 时,其会以 amount 作为增量增加 runningTotal 的值。

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

makeIncrementer 返回类型为 () -> Int。这意味着其返回的是一个函数,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 Int 类型的值。关于函数返回其他函数的内容,请查看函数类型作为返回类型。

makeIncrementer(forIncrement:) 函数定义了一个初始值为 0 的整型变量 runningTotal,用来存储当前总计数值。该值为 incrementer 的返回值。

makeIncrementer(forIncrement:) 有一个 Int 类型的参数,其外部参数名为 forIncrement,内部参数名为 amount,该参数表示每次 incrementer 被调用时 runningTotal 将要增加的量。makeIncrementer函数还定义了一个嵌套函数 incrementer,用来执行实际的增加操作。该函数简单地使 runningTotal 增加 amount,并将其返回。

如果我们单独考虑嵌套函数 incrementer(),会发现它有些不同寻常:

func incrementer() -> Int {
    runningTotal += amount
    return runningTotal
}

incrementer() 函数并没有任何参数,但是在函数体内访问了 runningTotalamount 变量。这是因为它从外围函数捕获了 runningTotalamount 变量的引用。捕获引用保证了 runningTotalamount 变量在调用完 makeIncrementer 后不会消失,并且保证了在下一次执行 incrementer 函数时,runningTotal 依旧存在。

注意 为了优化,如果一个值不会被闭包改变,或者在闭包创建后不会改变,Swift 可能会改为捕获并保存一份对值的拷贝。 Swift 也会负责被捕获变量的所有内存管理工作,包括释放不再需要的变量。

下面是一个使用 makeIncrementer 的例子:

let incrementByTen = makeIncrementer(forIncrement: 10)

该例子定义了一个叫做 incrementByTen 的常量,该常量指向一个每次调用会将其 runningTotal 变量增加 10incrementer 函数。调用这个函数多次可以得到以下结果:

incrementByTen()
// 返回的值为10
incrementByTen()
// 返回的值为20
incrementByTen()
// 返回的值为30

如果你创建了另一个 incrementer,它会有属于自己的引用,指向一个全新、独立的 runningTotal 变量:

let incrementBySeven = makeIncrementer(forIncrement: 7)
incrementBySeven()
// 返回的值为7

再次调用原来的 incrementByTen 会继续增加它自己的 runningTotal 变量,该变量和 incrementBySeven中捕获的变量没有任何联系:

incrementByTen()
// 返回的值为40

注意:
如果你将闭包赋值给一个类实例的属性,并且该闭包通过访问该实例或其成员而捕获了该实例,你将在闭包和该实例间创建一个循环强引用

原文出自51Swift转载请保留原文链接

你可能感兴趣的:(Swift-尾随闭包、值捕获)