swift 闭包(闭包表达式、尾随闭包、逃逸闭包、自动闭包)

闭包是自含的函数代码块,可以在代码中被传递和使用


闭包和swift的对比

  • Swift 中闭包与OC的 block 比较相似
  • Swift中闭包是一个特殊函数,OC中block是一个匿名函数
  • 闭包和block都经常用于回调
block表达式
类型:返回值类型(^block的名称)(block的参数)

返回值(^block变量名)(形参列表) =  ^( 形参列表){
        // 执行代码
};
NSString* (^blockName)(NSString *str) = ^(NSString *str){
    return str;
};
NSLog(@"%@",blockName(@"block"));
闭包表达式
类型:    (参数)->(返回值类型)

闭包名称 = { (形参列表) -> return 返回值 in
      // 执行代码  
}
let closure = { (str:String) -> String in
        return str
}
print(closure("closure"))

闭包类型

//无参数无返回值
let closure = {()->Void in
     print("closure")
}
closure()
//有参数无返回值
let closure = {(str:String) -> Void in
    print(str)
}
closure("closure")
//无参数有返回值
let closure = {() -> String in
    return "closure"
}
print(closure())
//有参数有返回值
let closure = { (str:String) -> String in
    return str
}
print(closure("closure"))

下面例子通过使用几次迭代展示了 sorted(by:)方法的定义和语法优化的方式。每一次迭代都用更简洁的方式描述了相同的功能

通过函数处理
  • sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔值来进行排序
  • 排序闭包函数类型需为:(Int, Int) -> Bool
let numbers = [1,9,2,8,3,7,4,6];
let numArr = numbers.sorted(by: callback)
print(numArr)    //[1, 2, 3, 4, 6, 7, 8, 9]

func callback(_ num1:Int,_ num2:Int) -> Bool {
    return num1 < num2
}
通过闭包表达式处理

内联闭包参数和返回值类型声明与 callback(::) 函数类型声明相同

let numArr1 = numbers.sorted { (num1:Int, num2:Int) -> Bool in
    return num1 < num2
}
print (numArr1)    //[1, 2, 3, 4, 6, 7, 8, 9] 
根据上下文推断类型

sorted(by:)方法被一个Int数组调用,因此其参数必须是 (Int, Int) -> Bool类型的函数,因为所有的类型都可以被正确推断,所以返回箭头(->)和围绕在参数周围的括号也可以被省略

let numArr2 = numbers.sorted { num1, num2 in
    return num1 < num2
}
print(numArr2)    //[1, 2, 3, 4, 6, 7, 8, 9]
单表达式闭包隐式返回

sorted(by:) 方法的参数类型明确了闭包必须返回一个 Bool 类型值,因为闭包函数体只包含了一个单一表达式(s1 > s2),该表达式返回Bool 类型值,因此这里没有歧义,return 关键字可以省略

let numArr3 = numbers.sorted { num1,num2 in num1 < num2 }
print(numArr3)    //[1, 2, 3, 4, 6, 7, 8, 9]
参数名称缩写
  • 可以直接通$0,$1,$2 来顺序调用闭包的参数,以此类推
  • 如果你在闭包表达式中使用参数名称缩写,你可以在闭包定义中省略参数列表,并且对应参数名称缩写的类型会通过函数类型进行推断。in关键字也同样可以被省略,因为此时闭包表达式完全由闭包函数体构成
let numArr4 = numbers.sorted {$0 < $1}
print(numArr4)   //[1, 2, 3, 4, 6, 7, 8, 9]
运算符方法

Swift 的 Int类型定义了关于大于号(>)的实现,其作为一个函数接受两个 Int 类型的参数并返回 Bool 类型的值。而这正好与 sorted(by:) 方法的参数需要的函数类型相符合。因此,可以简单地传递一个大于号

 let numArr5 = numbers.sorted(by: <)
 print(numArr5)

尾随闭包

  • 如果闭包是函数的最后一个参数,那么可以将闭包写在()后面,
func closure(num:Int,a:() -> ()) -> Void {
    print("多个参数")
}
//调用
closure(num: 3) {
    print("闭包为最后一个参数")
}
  • 如果函数只有一个参数且为闭包,那么()可以不写
func closure(a:() -> ()) -> Void {
    print("只有一个参数且在最后")
}
//调用
closure {
    print("闭包为最后一个参数")
}

值捕获

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

func add(num:Int) -> ()->Int {
    var value = 0
    func result() -> Int{
        value += num
        return value
    }
    /*
     这有一个叫做 add 的函数,其包含了一个叫做 result 的嵌套函数
     嵌套函数result()从上下文中捕获了两个值,value 和 num
     捕获这些值之后,add 将 result 作为闭包返回
     每次调用 result 时,其会以 num 作为增量增加 value 的值
     */
    return result
}
  • 嵌套函数可以捕获其外部函数所有的参数以及定义的常量和变量,并且保证了在下一次执行函数时,之前捕获的值依旧存在
let result = add(num: 10)
print(result())  //10
print(result())  //20
print(result())  //30
  • 同一个方法中的变量, 会被绑定到属于自己的变量
//如果你创建了另一个result1,它会有属于自己的引用,指向一个全新、独立的value变量
let result1 = add(num: 10)
print(result1())  //10
//再次调用原来的result会继续增加它自己的value变量,该变量和result1中捕获的变量没有任何联系
print(result())  //40

闭包是引用类型

  • 函数和闭包都是引用类型
    你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用
//这两个常量或变量都引用相同的闭包
let method = result

逃逸闭包

  • 一个传入函数的闭包如果在函数执行结束之后才会被调用,那么这个闭包就叫做逃逸闭包 (通俗点讲,不在当前方法中使用闭包,而是在方法之外使用)
  • 定义函数的参数为逃逸闭包时,只需要在参数名之前标注 @escaping,用来指明这个闭包是允许“逃逸”出这个函数的
  • 将一个闭包标记为@escaping意味着你必须在闭包中显式地引用self
var result: ()->Void = {}
var str = ""
func show1(closure: @escaping () -> Void) {
    result = closure
}
func show2(closure: () -> Void) {
    closure()
}
func doSomething() {
    show1 {self.str = "我是逃逸闭包"}
    show2 {str = "我是普通闭包"}
}

//调用
doSomething()
print(str)    //我是普通的闭包
result()    
print(str)    //我是逃逸的闭包

逃逸闭包是在函数执行之后再执行,于是这段代码最后输出“我是逃逸的闭包”


自动闭包

  • 自动闭包:自动创建一个闭包用来包裹一个表达式,这种闭包不接受任何参数,当闭包被调用时,返回包裹在闭包中的表达式的值
  • 自动闭包让你能够延迟求值,因为直到你调用这个闭包,代码段才会被执行
  • 这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显式的闭包
var arr = ["a","b","c"]
print(arr.count)  //3
let block = {
    arr.remove(at: 0)
}
print(arr.count)  //3
print(block())    //a
print(arr.count)  //2
  • 将闭包作为参数传递给函数时,同样可以延时求值

函数接受一个显式闭包类型的参数

func delete(closure: ()->String){
    print(closure())
}

var arr = ["a","b","c"]
delete(closure:{arr.remove(at: 0)})    //a

通过将参数标记为 @autoclosure 来接收一个自动闭包,该函数接受的是 String 类型参数而非闭包

func delete(closure: @autoclosure ()-> String){
    print(closure())
}

var arr = ["a","b","c"]
delete(closure:arr.remove(at: 0))  //a
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容