swift- 闭包一

/*
• 闭包表达式
• 尾随闭包
• 值捕获
• 闭包是引用类型
• 逃逸闭包
• 自动闭包
*/

    //闭包表达式
    //闭包表达式是一种利用简洁语法构建内联闭包的方式。闭包表达式提供了一些语法优化,使得撰写闭包变得简单 明了。下面闭包表达式的例子通过使用几次迭代展示了 sorted(by:) 方法定义和语法优化的方式。每一次迭代 都用更简洁的方式描述了相同的功能。
    
    //sorted 方法
    //sorted(by:) 的方法,它会根据你所提供的用于排序的闭包函数将已知类型数组中的 值进行排序。一旦排序完成,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排 序的新数组。原数组不会被 sorted(by:) 方法修改。
   // 下面的闭包表达式示例使用 sorted(by:) 方法对一个 String 类型的数组进行字母逆序排序。以下是初始数 组:
    let names = ["Chris","Alex","Ewa","Barry","Daniella"]
    
    //sorted(by:) 方法接受一个闭包,该闭包函数需要传入与数组元素类型相同的两个值,并返回一个布尔类型值来 表明当排序结束后传入的第一个参数排在第二个参数前面还是后面。如果第一个参数值出现在第二个参数值前 面,排序闭包函数需要返回 true ,反之返回 false 。
    
    func backward(_ s1:String, _ s2:String) -> Bool {
    
        return s1 > s2
    }
    var reversedNames = names.sorted(by: backward)
    
    print(reversedNames)
    
  
    //闭包的表达式语法
    // 闭包表达式语法有如下的一般形式:
//        { (parameters) -> returnType in
//            statements
//        }
    
    //下面的例子展示了之前 backward(_:_:) 函数对应的闭包表达式版本的代码
    reversedNames = names.sorted(by: {
    
        (s1: String, s2: String) -> Bool in
        return s1 > s2
    })
    
    //根据上下文判断类型
    reversedNames = names.sorted(by: {
    
        s1, s2 in
        return s1>s2
    })
    
    //单表达式闭包可以通过省略 return 关键字来隐式返回单行表达式的结果
    reversedNames = names.sorted(by: {
    
        s1, s2 in s1 > s2
    })
    
  //参数名称缩写
    reversedNames = names.sorted(by: {$0 > $1})
    
    //运算符方法
    reversedNames = names.sorted(by: >)
    
    //尾随闭包
    //如果你需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用尾随闭包来增强函数的可读性。尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。在使用尾随闭包时,你不用写出它的参数标签:
    func someFunctionThatTakesAClousure(closure: () -> Void) {
    
        //函数体部分
    }
    
    
    //以下是不使用尾随闭包进行函数调用
    someFunctionThatTakesAClousure(closure: {
       //闭包主体部分
    })
    
    //以下是使用尾随闭包进行函数调用
    someFunctionThatTakesAClousure() {
        //闭包主体部分
    }
    
    
    //方法参数的字符串排序闭包可以改写为:
    reversedNames = names.sorted() {$0 > $1}
    
    //如果闭包表达式是函数或方法的唯一参数,则当你使用尾随闭包时,你甚至可以把 () 省略掉:
    reversedNames = names.sorted {$0 > $1}
    
    //当闭包非常长以至于不能在一行中进行书写时,尾随闭包变得非常有用。举例来说,Swift 的 Array 类型有一 个 map(_:) 方法,这个方法获取一个闭包表达式作为其唯一参数。该闭包函数会为数组中的每一个元素调用一 次,并返回该元素所映射的值。具体的映射方式和返回值类型由闭包来指定。
    //当提供给数组的闭包应用于每个数组元素后,map(_:) 方法将返回一个新的数组,数组中包含了与原数组中的元 素一一对应的映射后的值。
    //下例介绍了如何在 map(_:) 方法中使用尾随闭包将 Int 类型数组 [16, 58, 510] 转换为包含对应 String 类型的值的数组 ["OneSix", "FiveEight", "FiveOneZero"] :
    
    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 strings = numbers.map {
        (number) -> String in
        var number = number
        var output = ""
        repeat {
        
            output = digitNames[number % 10]! + output
            number /= 10
        }while number > 0
        
        return output
    }
    
    //map(_:) 为数组中每一个元素调用了一次闭包表达式。你不需要指定闭包的输入参数 number 的类型,因为可 以通过要映射的数组类型进行推断。
    
    //值捕获:闭包可以在其被定义的上下文中捕获常量或变量。即使定义这些常量和变量的原作用域已经不存在,闭包仍然可以在闭包函数体内引用和修改这些值。
    func makeIncrementer(forIncrement amount: NSInteger) -> () -> NSInteger{
    
        var runningTotal = 0
        func incrementer() -> NSInteger {
        
            runningTotal += amount
            return runningTotal
        }
        return incrementer
    }
    
    
    let incrementByTen = makeIncrementer(forIncrement: 10)
    incrementByTen() // 返回的值为10 
    incrementByTen() // 返回的值为20 
    incrementByTen() // 返回的值为30
    
    let incrementBySeven = makeIncrementer(forIncrement: 7)
    incrementBySeven()// 返回的值为7
    
    //闭包是引用类型:无论你将函数或闭包赋值给一个常量还是变量,你实际上都是将常量或变量的值设置为对应函数或闭包的引用
    let alsoIncrementByTen = incrementByTen
    alsoIncrementByTen()// 返回的值为50
    print(alsoIncrementByTen())
    
    //逃逸闭包
    //当一个闭包作为参数传到一个函数中,但是这个闭包在函数返回之后才被执行,我们称该闭包从函数中逃逸。当你定义接受闭包作为参数的函数时,你可以在参数名之前标注 @escaping ,用来指明这个闭包是允许“逃逸”出这个函数的。一种能使闭包“逃逸”出函数的方法是,将这个闭包保存在一个函数外部定义的变量中。

    var completionHandlers: [() -> Void] = []
    func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
        completionHandlers.append(completionHandler)
    }
    //someFunctionWithEscapingClosure(_:) 函数接受一个闭包作为参数,该闭包被添加到一个函数外定义的数组 中。如果你不将这个参数标记为 @escaping ,就会得到一个编译错误。
    
    //将一个闭包标记为 @escaping 意味着你必须在闭包中显式地引用 self 。比如说,在下面的代码中,传递到 s omeFunctionWithEscapingClosure(_:) 中的闭包是一个逃逸闭包,这意味着它需要显式地引用 self 。相对 的,传递到 someFunctionWithNonescapingClosure(_:) 中的闭包是一个非逃逸闭包,这意味着它可以隐式引用self 。
    func someFunctionWithNonescapingClosure(closure: () -> Void) {
        closure()
    }
    class SomeClass {
        var x = 10
        func doSomething() {
//                someFunctionWithEscapingClosure { self.x = 100 }
//                someFunctionWithNonescapingClosure { x = 200 }
        }
    }
    let instance = SomeClass()
    instance.doSomething()
    print(instance.x)
    // 打印出 "200"
    completionHandlers.first?()
    print(instance.x)
    // 打印出 "100"
    
    
    //自动闭包
    //自动闭包是一种自动创建的闭包,用于包装传递给函数作为参数的表达式。这种闭包不接受任何参数,当它被调用的时候,会返回被包装在其中的表达式的值。这种便利语法让你能够省略闭包的花括号,用一个普通的表达式来代替显示的闭包
    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    print(customersInLine.count)
    
    let customerProvider = { customersInLine.remove(at: 0) }
    print(customersInLine.count)
    print("Now serving \(customerProvider())!")
    // Prints "Now serving Chris!" print(customersInLine.count)
    // 打印出 "4"
    // customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
    func serve(customer customerProvider: () -> String) {
        print("Now serving \(customerProvider())!")
    }
    serve(customer: { customersInLine.remove(at: 0) } ) // 打印出 "Now serving Alex!"
    
   // 如果你想让一个自动闭包可以“逃逸”,则应该同时使用 @autoclosure 和 @escaping 属性。
    
    // customersInLine i= ["Barry", "Daniella"]
    var customerProviders: [() -> String] = []
    func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
        customerProviders.append(customerProvider)
    }
    collectCustomerProviders(customersInLine.remove(at: 0))
    collectCustomerProviders(customersInLine.remove(at: 0))
    print("Collected \(customerProviders.count) closures.") // 打印 "Collected 2 closures."
    for customerProvider in customerProviders {
        print("Now serving \(customerProvider())!")
    }
    // 打印 "Now serving Barry!"
    // 打印 "Now serving Daniella!"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容