Swift3.0 闭包(blcok)的全面介绍及使用

Easter79
• 阅读 739

闭包是自包含的函数代码块,可以在代码中被传递和使用。Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些编程语言中的匿名函数比较相似。闭包可以捕获和存储其所在上下文中任意常量和变量的引用。被称为包裹常量和变量。 Swift 会为你管理在捕获过程中涉及到的所有内存操作。

闭包表达式语法有如下的一般形式:

 {
    (parameters) -> returnType in
      statements
 }
  • 这里的参数(parameters),可以是in-out(输入输出参数),但不能设定默认值。如果是可变参数,必须放在最后一位,不然编译器报错。元组也可以作为参数或者返回值。
  • 闭包的函数体部分由关键字in引入。该关键字表示闭包的参数和返回值类型定义已经完成,闭包函数体即将开始

例如:

 let  sum:(Int , Int) -> Int = {(a : Int ,b : Int) -> Int in
          return a + b  
}
 print(sum(3,4))

由于可以推断出闭包的参数和返回值类型,所以闭包表达式可以表达为:

 let  sum:( Int , Int ) -> Int = { a , b  in
          return a + b 
 }

单表达式闭包隐式返回

单行表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果,如上版本的例子可以改写为: 

 let  sum:( Int , Int ) -> Int = { a , b  in
          a + b 
 }

有参数没有返回值的写法

let say0:(String) ->Void = {
    (name: String) in
    print("hi \(name)")
}
say0("cdh")
//输出结果:  hi cdh

没有参数没有返回值写法

let say1:() ->Void = {
    () -> () in // 当没有参数也没有返回值是这行都可以省略
    print("hi cdh")
}
say1()
//输出结果:  hi cdh

闭包的声明

Swift用typealias声明闭包数据类型,类似于Objective-C中的typedef

typealias  TestBlcok = (Int , Int) -> Int

let test : TestBlcok = {( a , b ) in  a + b}

let result = test( 12 , 20 )

计算结果是 32

尾随闭包

若将闭包作为函数最后一个参数,可以省略参数标签,然后将闭包表达式写在函数调用括号后面

func testFunction(testBlock: ()->Void){
    //这里需要传进来的闭包类型是无参数和无返回值的
    testBlock()
}
//正常写法
testFunction(testBlock: {
    print("正常写法")
})
//尾随闭包写法
testFunction(){
    print("尾随闭包写法")
}
//也可以把括号去掉,也是尾随闭包写法。推荐写法
testFunction { 
    print("去掉括号的尾随闭包写法")
}

值捕获

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

Swift 中,可以捕获值的闭包的最简单形式是嵌套函数,也就是定义在其他函数的函数体内的函数。嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量。”

 例如:

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

makeIncrementor 返回类型为 () -> Int。这意味着其返回的是一个函数,而非一个简单类型的值。该函数在每次调用时不接受参数,只返回一个 Int 类型的值。

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

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

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

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

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

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

闭包引起的循环强引用

//定义一个全局变量:

        var a: Int = 9

        var closure: ()->Void = {

            /**

             默认闭包会对它访问的对象执行强引用。
             这个地方访问了self ,导致调用 该闭包的时候引用计数+1
             如果闭包属性中没有直接或者间接访问self,就不会产生循环强引用。
             可以使用unowned或者weak 来修饰self。
             当然两种存在区别。Swift中的weak、unowned对应Objective-C中的weak、unsafe_unretained,
             这表明前者在对象释放后 会自动将对象置为nil,而后者依然保持一个“无效的”引用,如果此时调用这
             个“无效的”引用将会引起程序崩溃。对于后者的使用,我们必须保证访问的时候对象没有释放。
             相反,weak则友好一点我们也更加习惯使用它,

             */

       [weak self] in

       print("a=\(self.a)")

}

逃逸闭包

当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的。

自动闭包

自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包。

自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行。延迟求值对于那些有副作用(Side Effect)和高计算成本的代码来说是很有益处的,因为它使得你能控制代码的执行时机。

点赞
收藏
评论区
推荐文章
Souleigh ✨ Souleigh ✨
3年前
JS - 从执行上下文的角度来理解闭包
今天看到一篇关于闭包的文章,里面有这样一句话“就我而言对于闭包的理解仅止步于一些概念,看到相关代码知道这是个闭包,但闭包能解决哪些问题场景我了解的并不多”,这说的不就是我么,每每在面试中被问及什么是闭包,大部分情况下得到的答复是(至少我以前是)A函数嵌套B函数,B函数使用了A函数的内部变量,且A函数返回B函数,这就是闭包。而往往面试官想要听到的并不是这样的
Jacquelyn38 Jacquelyn38
3年前
你不可不知的JS面试题(第三期)
1、什么是闭包?如图所示,闭包就是一个定义在函数内部的函数,其作用是将函数内部和函数外部连接起来。大家知道,作用域的问题,就是在函数内部定义的变量称为局部变量,外部取不到值。下面我们通过代码来更加详细地看一下:function A()       let x  1;       return function B()           c
Easter79 Easter79
3年前
swift闭包表达式和尾随闭包
我们从一个Swift函数说起。并以此为例子。Swift的标准库提供了一个叫做sorted(by:)的方法,会根据你提供的排序闭包将已知类型的数组的值进行排序。一旦它排序完成,sorted(by:)方法会返回与原数组类型大小完全相同的一个新数组,该数组的元素是已排序好的。原始数组不会被sorted(by:)方法修改。我们以soted方法为例子
Souleigh ✨ Souleigh ✨
4年前
Swift 简介
Swift和ObjectiveC的主要区别1,编程范式Swift可以面向协议编程、函数式编程、面向对象编程。Swift语言引入了协议、协议的扩展、泛型等新特性,因此使用Swift语言可以很好地面向协议编程;Swift语言将函数和闭包提升为语言的一等公民,函数可以作为一个变量、可以作为其他函数的参数、作为其他函数的返回值等来传递,所以
Bill78 Bill78
3年前
Python 中的闭包
闭包定义:如果在一个内部函数里,对在外部作用于(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包Python中的闭包原文出处:田小计划(http://www.cnblogs.com/wilber2013/p/4658894.html)闭包(closure)是函数式编程的重要的语法结构
菜园前端 菜园前端
1年前
一篇文章教会你什么是闭包
原文链接:什么是闭包?闭包的概念并不复杂,但是它的定义比较绕(就像平时经常用到它,却又说不出来是什么)。可以在一个作用域中调用函数的内部函数并访问到该函数中的作用域的成员,这就是闭包。给一个建议,网上闭包的概念可以搜出来一大堆,但是你真的了解它吗?你有去调
Wesley13 Wesley13
3年前
JS 闭包(内存溢出与内存泄漏)(垃圾回收机制)
1.有关闭包定义闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量闭包的特性:函数内再嵌套函数内部函数可以引用外层的参数和变量参数和变量不会被垃圾回收机制回收
Easter79 Easter79
3年前
Swift讲解专题八——闭包
Swift讲解专题八——闭包一、引言      Swift中的闭包是有一定功能的代码块,这十分类似于ObjectiveC中的block语法。Swift中的闭包语法风格十分简洁,其作用和函数的作用相似。二、从一个系统函数看闭包      Swift标准函数库中提供了一个sort排序函数,对
Stella981 Stella981
3年前
JavaScript函数——闭包
闭包概念只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁例子functionouter(){varlocalVal30;returnlocalVal;}
Wesley13 Wesley13
3年前
go 学习笔记之10 分钟简要理解 go 语言闭包技术
闭包是主流编程语言中的一种通用技术,常常和函数式编程进行强强联合,本文主要是介绍Go语言中什么是闭包以及怎么理解闭包.如果读者对于Go语言的闭包还不是特别清楚的话,可以参考上一篇文章go学习笔记之仅仅需要一个示例就能讲清楚什么闭包(https://www.oschina.net/ac
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k