Swift基础语法: 23 - Swift的Trailing闭包, 捕获, 闭包是引用类型

前面我们基本上了解了Swift的闭包简介, 现在让我们来继续讲解闭包的其他内容:


1.Trailing闭包

在前面我们知道了简单的闭包是怎么样运用的, 但在实际开发中, 我们不可能只使用简单的闭包, 所以这时候就出现了第二种闭包的写法, 那就是Trailing闭包, 让我们来看看例子:

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]

let strings = numbers.map {
    (var number) -> String in

    var output = ""

    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10
    }
    return output
}

println(strings)
// 打印出来的结果: [OneSix, FiveEight, FiveOneZero]

PS:
1.在这个例子中, strings 常量被推断为字符串类型数组,即 [String], map 在数组中为每一个元素调用了闭包表达式, 您不需要指定闭包的输入参数 number 的类型,因为可以通过要映射的数组类型进行推断, 闭包表达式在每次被调用的时候创建了一个字符串并返回。其使用求余运算符 (number %10) 计算最后一位数字并利用 digitNames 字典获取所映射的字符串.

2.字典 digitNames 下标后跟着一个叹号 (!), 因为字典下标返回一个可选值 (optional value), 表明即使该 key 不存在也不会查找失败, 在上例中, 它保证了 number % 10 可以总是作为一个 digitNames 字典的有效下标 key, 因此叹号可以用于强展开 (force-unwrap) 存储在可选下标项中的 String 类型值.


2.捕获

在我们使用闭包的时候, 其实我们还可以捕获我们自己定义的常量或者变量, 即使里面的常量和变量的作用域不存在, 闭包仍然可以在闭包函数体内引用或者修改, Swift 最简单的闭包形式是嵌套函数,也就是定义在其他函数体内的函数, 嵌套函数可以捕 获其外部函数所有的参数以及定义的常量和变量, 让我们一起来看看:

func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0

    func incrementor() -> Int {

        runningTotal += amount

        return runningTotal
    }
    return incrementor
}

let incrementBySeven = makeIncrementor(forIncrement: 7)
let a = incrementBySeven()
println(a)
// 打印出来的结果: 7

PS:
1.makeIncrementor 返回类型为 () -> Int, 这意味着其返回的是一个函数,而不是一个简单类型值, 该函数在每次调用时不接受参数只返回一个 Int 类型的值, 关于函数返回其他函 数的内容,请查看 Function Types as Return Types.

2.makeIncrementor 函数定义了一个整型变量 runningTotal (初始为 0) 用来存储当前增加总数, 该值通过 incrementor 返回.

3.makeIncrementor 有一个 Int 类型的参数, 其外部命名为 forIncrement, 内部命名为 amount, 表示每次 incrementor 被调用时 runningTotal 将要增加的量.

4.incrementor 函数用来执行实际的增加操作, 该函数简单地使 runningTotal 增加 amount, 并将其返回.

5.Swift 会决定捕获引用还是拷贝值, 您不需要标注 amount 或者 runningTotal 来声明在嵌入的 incrementor 函数中的使用方式, Swift 同时也处理 runingTotal 变量的内存管理操作, 如果不再被 incrementor 函数使用, 则会被清除.

还有另外一个例子也很好玩, 比如:

let incrementBySeven = makeIncrementor(forIncrement: 7)
let a = incrementBySeven()
let b = incrementBySeven()
let c = incrementBySeven()
println("\(a), \(b), \(c)")
// 打印出来的结果: 7, 14, 21

上面已经很好的解释了, 这里我就不多做解释了, 还有一个注意点, 比如:

let incrementBySeven = makeIncrementor(forIncrement: 7)
let a = incrementBySeven()
let b = incrementBySeven()
let c = incrementBySeven()
println("\(a), \(b), \(c)")
// 打印出来的结果: 7, 14, 21

let incrementByTen = makeIncrementor(forIncrement: 10)
let A = incrementByTen()
let B = incrementByTen()
let C = incrementByTen()
println("\(A), \(B), \(C)")
// 打印出来的结果: 10, 20, 30

虽然这两个常量调用的是同一个闭包, 但是它们之间所捕获的内容并没有关联, 这个需要注意.


3.闭包是引用类型


上面的例子中, incrementBySeven 和 incrementByTen 是常量, 但是这些常量指向的闭包仍然可以增加其捕获的变量值. 这是因为函数和闭包都是引用类型.

无论您将函数/闭包赋值给一个常量还是变量, 您实际上都是将常量/变量的值设置为对应函数/闭包的引用, 上面的例子中, incrementByTen 指向闭包的引用是一个常量, 而并非闭包内容本身, 比如:

let alsoIncrementByTen = incrementByTen()
println(alsoIncrementByTen)
// 打印出来的结果: 40

好了, 这次我们就讲到这里, 下次我们继续~

posted @ 2015-03-17 10:21  背着吉他去流浪  阅读(307)  评论(0编辑  收藏  举报