深入理解Swift语言中的闭包机制

在 Swift 中的闭包类似于结构块,并可以在任何地方调用,它就像 C 和 Objective C 语言内置的函数。 函数内部定义的常数和变量引用可被捕获并存储在闭包。函数被视为封闭的特殊情况,它有 3 种形式。

在 Swift 语言闭合表达式,如下优化,重量轻语法风格,其中包括:

  • 推导参数并从上下文菜单返回值的类型
  • 从单封表达的隐性返回
  • 简略参数名称
  • 尾部闭包语法

语法
下面是一个通用的语法定义用于闭包,它接受参数并返回数据的类型:

代码如下:

{(parameters) -> return type in
   statements
}

下面是一个简单的例子:

代码如下:

let studname = { println("Welcome to Swift Closures") }
studname()

当我们使用 playground 运行上面的程序,我们得到以下结果

Welcome to Swift Closures

以下闭包接受两个参数并返回一个布尔值:

代码如下:

{(Int, Int) -> Bool in
   Statement1
   Statement 2
    ---
   Statement n
}

下面是一个简单的例子:

代码如下:

let divide = {(val1: Int, val2: Int) -> Int in
   return val1 / val2
}
let result = divide(200, 20)
println (result)

当我们使用 playground 运行上面的程序,我们得到以下结果

10

在闭包中的表达式
以便捷的方式命名来定义代码块可以通过嵌套函数实现的。取而代之代表整个函数声明及名称构造用来表示函数。代表函数的语法清晰,简短声明是通过封闭的表达来实现的。

升序排列程序
排序字符串是 Swift 中保留的函数 “sorted”,这是在标准库中已提供实现。该函数将所述给定的字符串进行递增顺序排序并返回具有相同的尺寸,并在旧数组中相同数据类型的一个新的数组的元素。旧的数组保持不变。

两个参数的排序在函数内部表示:

已知类型的值表示为数组

数组的内容 (Int,Int) ,并返回一个布尔值(Bool),如果数组排序不好就会返回true,否则将返回false。

普通函数带输入字符串被写入,并传递给排序函数获得字符到新的数组,如下面所示:

代码如下:

func ascend(s1: String, s2: String) -> Bool {
   return s1 > s2
}
let stringcmp = ascend("swift", "great")
println (stringcmp)

当我们使用 playground 运行上面的程序,我们得到以下结果

代码如下:

true

最初的数组排序给定为 "Swift" 和 "great"。函数用来数组排序被声明为字符串数据类型,并且返回类型为布尔型。 两个字符串进行比较,并以升序排序,并存储在新的数组。如果排序执行成功,该函数将返回true;否则将返回 false。

闭包表达式语法用法

常量参数:

可变参数 和 inout 参数

闭包表达不支持的默认值。可变参数和参数元组也可以用来作为参数类型和返回类型。

代码如下:

let sum = {(no1: Int, no2: Int) -> Int in
   return no1 + no2
}
let digits = sum(10, 20)
println(digits)

当我们使用 playground 运行上面的程序,我们得到以下结果

30

在函数声明中提到的参数和返回类型声明,也可通过使用 'in' 关键字内联闭包表达式函数表示。 一旦声明参数及其返回类型“in”关键字,则用于表示闭包体。

单一表达式隐式返回
在这里,排序函数的第二个参数的函数类型明确指出,一个布尔值必须由闭包返回。因为闭包体内含有一个表达式(s1 > s2)返回一个布尔值, 不会出现歧义,其返回关键字可以省略。

要返回一个表达式语句在闭包中, “return” 关键字在其声明部分被省略。

代码如下:

let count = [5, 10, -6, 75, 20]
var descending = sorted(count, { n1, n2 in n1 > n2 })
var ascending = sorted(count, { n1, n2 in n1 < n2 })

println(descending)
println(ascending)

当我们使用 playground 运行上面的程序,我们得到以下结果

[75, 20, 10, 5, -6]
[-6, 5, 10, 20, 75]

该语句本身明确规定,当 string1 大于 string2 返回 true,否则为false,因此return语句省略。

已知类型的闭包
考虑两个数相加。我们知道相加后将返回整数数据类型。因此,已知类型的闭包声明

代码如下:

let sub = {(no1: Int, no2: Int) -> Int in
   return no1 - no2
}
let digits = sub(10, 20)
println(digits)

当我们使用 playground 运行上面的程序,我们得到以下结果

-10

声明简写参数名称作为闭包
Swift 自动提供简写参数名内联闭包, 可以使用由 $0,$1,$2 等等名称,指的是封闭的参数值。

代码如下:

var shorthand: (String, String) -> String
shorthand = { $1 }
println(shorthand("100", "200"))

在这里,$0 和 $1 参考闭包的第一和第二个字符串参数。

当我们使用 playground 运行上面的程序,我们得到以下结果

200

Swift 方便用户来表示内嵌闭包为缩写参数名为:$0, $1, $2 --- $n.

闭包参数列表中被省略定义部分,当我们表示内部闭包表达式简写参数名。 根据函数类型简写参数名称将被导出。由于简写参数表达体所定义的 'in' 关键字被省略。

闭包作为操作函数
Swift 提供了一种简单的方法访问的成员,只需提供操作符函数作为闭包。 在前面的例子关键字“Bool”是用来比较两个字符串,相等返回“true”,否则返回“false”。

表达式即使在闭包中变得简单在操作函数:

代码如下:

let numb = [98, -20, -30, 42, 18, 35]
var sortedNumbers = numb.sorted({
   (left: Int, right: Int) -> Bool in
      return left < right
})
let asc = numb.sorted(<)
println(asc)

当我们使用 playground 运行上面的程序,我们得到以下结果

[-30, -20, 18, 35, 42, 98]


闭包作为尾随包
传递这个函数的最后一个参数到闭合表达式使用“尾随闭包”声明。它使用 {} 写在函数()外部。当它不能写入函数内联在一行上,使用它是需要。

代码如下:

reversed = sorted(names) { $0 > $1}

其中 {$0 > $1} 表示为外部(名称)声明尾随闭包。

代码如下:

import Foundation
var letters = ["North", "East", "West", "South"]
 
let twoletters = letters.map({ (state: String) -> String in
   return state.substringToIndex(advance(state.startIndex, 2)).uppercaseString
})
let stletters = letters.map() { $0.substringToIndex(advance($0.startIndex, 2)).uppercaseString }
println(stletters)

当我们使用 playground 运行上面的程序,我们得到以下结果

[NO, EA, WE, SO]

捕获值和引用类型
在闭包的帮助下 Swift 完成捕捉常量和变量的值。它还参考修改值,即使常量和变量在闭包体已经不存。

捕获常数和变量值是通过使用嵌套函数写入函数,这是使用其它函数体来实现的:

  • 一个嵌套函数捕获
  • 外部函数参数
  • 捕捉常量和外部函数中定义的变量

Swift 中当常量或变量在函数中声明,引用到变量也自动地被闭合创建。它也提供工具来引用两个以上的变量作为同一闭合如下:

代码如下:

let decrem = calcDecrement(forDecrement: 18)
decrem()

在这里,oneDecrement 和 递减变量都指向同一个内存块闭合参考。

代码如下:

func calcDecrement(forDecrement total: Int) -> () -> Int {
   var overallDecrement = 100
   func decrementer() -> Int {
      overallDecrement -= total
      println(overallDecrement)
      return overallDecrement
   }
   return decrementer
}
let decrem = calcDecrement(forDecrement: 18)
decrem()
decrem()
decrem()

当我们使用 playground 运行上面的程序,我们得到以下结果:

82
64
46

当每一个外部函数 calcDecrement 调用时都会调用 decrementer()函数 并通过值 18 递减,并在外部函数 calcDecrement 的帮助下返回结果。在这里,calcDecrement 作为一个闭合。

即使函数 decrement()没有任何参数,闭合默认情况下是指变量的"整体递减“ “total” 通过获取其值。为指定的变量的值副本被使用新的 decrementer()函数存储。Swift 通过处理存储器管理功能分配和释放存储器空间当变量在不使用。

下面来一波总结~

代码如下:

/* 闭包(Closures)
 * 闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值。
 * 在Swift中的闭包与C、OC中的blocks和其它编程语言(如Python)中的lambdas类似。
 * 闭包可以捕获和存储上下文中定义的的任何常量和变量的引用。这就是所谓的变量和变量的自封闭,
 * 因此命名为”闭包“("Closures)").Swift还会处理所有捕获的引用的内存管理。
 *
 * 全局函数和嵌套函数其实就是特殊的闭包。
 * 闭包的形式有:
 * (1)全局函数都是闭包,有名字但不能捕获任何值。
 * (2)嵌套函数都是闭包,且有名字,也能捕获封闭函数内的值。
 * (3)闭包表达式都是无名闭包,使用轻量级语法,可以根据上下文环境捕获值。
 *
 * Swift中的闭包有很多优化的地方:
 * (1)根据上下文推断参数和返回值类型
 * (2)从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
 * (3)可以使用简化参数名,如$0, $1(从0开始,表示第i个参数...)
 * (4)提供了尾随闭包语法(Trailing closure syntax)
 */
 
 // 下面用Swift标准库中的sort方法来一步步简化闭包写法
 // sort函数需要两个参数
 // 参数一:数组
 // 参数二:一个闭包:带有两个参数,这两个参数类型与数组中的元素类型相同,返回值是Bool
 var names = ["Swift", "Arial", "Soga", "Donary"]
 
 // 第一种方式:使用函数
 func backwards(firstString: String, secondString: String) -> Bool {
   return firstString > secondString // 升序排序
 }
 // 这里第二个参数,传了一个函数
 // reversed is equal to ["Swift", "Soga", "Donary", "Arial"]
 var reversed = sort(nams, backwards)
 
 // 第二种方式:使用闭包方式
 // 完整闭包写法是在花括号内有参数列表和返回值,用关键字in表明闭包体的开始
 // (firstString: String, secondString: String) 闭包参数列表
 // -> Bool 指明闭包返回值类型是Bool
 // in关键字表明闭包体的开始
 reversed = sort(names, { (firstString: String, secondString: String) -> Bool in
    return firstString > secondString
 })

// 这里可以进一步简化写法,因为闭包代码比较短,可以写到一行上
 reversed = sort(names, { (firstString: String, secondString: String) -> Bool in return firstString > secondString})
 
// 下面再进一步简化写法 :根据环境上下文自动推断出类型
// 参数列表都没有指明类型,也没有指明返回值类型,这是因为swift可以根据上下文推测出
// firstString和secondString的类型会是names数组元素的类型,而返回值类型会根据return语句结果得到
reversed = sort(names, { firstString, secondString in return firstString > secondString})

// 再进一步简化:隐式返回(单行语句闭包)
// 因为闭包体只有一行代码,可以省略return
reversed = sort(names, { firstString, secondString in firstString > secondString})

// 再进一步简化:使用简化参数名($i,i=0,1,2...从0开始的)
// Swift会推断出闭包需要两个参数,类型与names数组元素相同
reversed = sort(names, { $0 > $1 })

// 最简单的一种写法:使用操作符
reversed = sort(names, >) 
 
 
/*
 * 尾随闭包(Trailing Closures)
 * 如果函数需要一个闭包参数作为参数,且这个参数是最后一个参数,而这个闭包表达式又很长时,
 * 使用尾随闭包是很有用的。尾随闭包可以放在函数参数列表外,也就是括号外。如果函数只有一个参数,
 * 那么可以把括号()省略掉,后面直接跟着闭包。
 */
// Array的方法map()就需要一个闭包作为参数
let strings = numbers.map { // map函数后面的()可以省略掉
  (var number) -> String in
  var output = ""
  while number > 0 {
    output = String(number % 10) + output
 number /= 10
  }
  return output
}
 
/* 捕获值
 * 闭包可以根据环境上下文捕获到定义的常量和变量。闭包可以引用和修改这些捕获到的常量和变量,
 * 就算在原来的范围内定义为常量或者变量已经不再存在(很牛逼)。
 * 在Swift中闭包的最简单形式是嵌套函数。
 */
func increment(#amount: Int) -> (() -> Int) {
  var total = 0
  func incrementAmount() -> Int {
    total += amount // total是外部函数体内的变量,这里是可以捕获到的
 return total
  }
  return incrementAmount // 返回的是一个嵌套函数(闭包)

 
// 闭包是引用类型,所以incrementByTen声明为常量也可以修改total
let incrementByTen = increment(amount: 10)
incrementByTen() // return 10,incrementByTen是一个闭包
// 这里是没有改变对increment的引用,所以会保存之前的值
incrementByTen() // return 20 
incrementByTen() // return 30

let incrementByOne = increment(amount: 1)
incrementByOne() // return 1
incrementByOne() // return 2 
incrementByTen() // return 40
incrementByOne() // return 3

(0)

相关推荐

  • 详解Swift中的函数及函数闭包使用

    一.引言 函数是有特定功能的代码段,函数会有一个特定的名称调用时来使用.Swift提供了十分灵活的方式来创建与调用函数.事实上在Swift,每个函数都是一种类型,这种类型由参数和返回值来决定.Swift和Objective-C的一大区别就在于Swift中的函数可以进行嵌套. 而Swift中的闭包是有一定功能的代码块,这十分类似于Objective-C中的block语法.Swift中的闭包语法风格十分简洁,其作用和函数的作用相似. 二.函数的创建与调用 函数通过函数名,参数和返回值来定义,参数和返

  • Swift中闭包实战案例详解

    前言 无论苹果的官方文档还是由官方文档衍生出来的一些文章和书籍都比较重视基础语法知识的讲解,对于实战中的应用提及的都很少,所以当我们想使用"闭包"解决一些问题的时候,会忽然出现看着一堆理论知识却不知从何下手的尴尬感,这就是理论和时实战的区别了. 本文不赘述Swift闭包的的基本语法了,百度或者Google下有很多资料.如题所示本文着重讲述Swift闭包的一些实战案例,有需要的小伙伴可以参考下,经验丰富的大神也请指教. 关于如何理解闭包 学习闭包的第一个难点就是理解闭包,可能很多人用了很

  • IOS swift3.0 下闭包语法整理

    IOS swift3.0 下闭包语法整理 一.闭包的概念 有oc基础的都知道,闭包其实是oc里面的block,语法格式不一样,但作用是一样的.主要是用于callBack(异步回调)或者两个类之间的通信.它的本质一个函数,一个可执行的代码块,只是这个函数是没有名字的,也就是匿名函数.你也可以把他看作如 int.float一样,是一种数据类型,一种可以作为参数传递的数据类型. 二.基本语法 1.闭包的声明 //定义一个求和闭包 //闭包类型:(Int,Int)->(Int) let add:(Int

  • Swift教程之闭包详解

    闭包(Closures)是独立的函数代码块,能在代码中传递及使用.Swift中的闭包与C和Objective-C中的代码块及其它编程语言中的匿名函数相似. 闭包可以在上下文的范围内捕获.存储任何被定义的常量和变量引用.因这些常量和变量的封闭性,而命名为"闭包(Closures)".Swift能够对所有你所能捕获到的引用进行内存管理. NOTE 假如你对"捕获(capturing)"不熟悉,请不要担心,具体可以参考Capturing Values(捕获值). 全局函数

  • Swift 3.0基础学习之闭包

    前言 闭包是功能性自包含模块,可以在代码中被传递和使用. Swift 中的闭包与 C 和 Objective-C中的 blocks 以及其他一些编程语言中的 lambdas 比较相似.下面这篇文章就来详细介绍了关于Swift 3.0中的闭包,感兴趣的一起来看看吧. 开始 闭包的书写格式如下: { (parameters) -> return type in statements } 如 reversedNames = names.sorted(by: { (s1: String, s2: Str

  • Swift 中闭包的简单使用

    本文主要是介绍Swift中闭包的简单使用,将从"闭包的定义"."闭包的创建.赋值.调用"."闭包常见的几种使用场景","使用闭包可能引起的循环强引用" 四个方面入手,重点介绍闭包如何使用,没有高深的概念,只是专注于实际使用,属于入门级水平,后面还会有关于闭包更加详细和深入理解的文章.希望大家在阅读完本文后能够对闭包有一个整体的理解以及能够简单的使用它. 闭包的定义 在Swift开发文档中是这样介绍闭包的:闭包是可以在你的代码中

  • 深入理解Swift语言中的闭包机制

    在 Swift 中的闭包类似于结构块,并可以在任何地方调用,它就像 C 和 Objective C 语言内置的函数. 函数内部定义的常数和变量引用可被捕获并存储在闭包.函数被视为封闭的特殊情况,它有 3 种形式. 在 Swift 语言闭合表达式,如下优化,重量轻语法风格,其中包括: 推导参数并从上下文菜单返回值的类型 从单封表达的隐性返回 简略参数名称 尾部闭包语法 语法 下面是一个通用的语法定义用于闭包,它接受参数并返回数据的类型: 复制代码 代码如下: {(parameters) -> re

  • 深入理解Go语言中的闭包

    闭包 在函数编程中经常用到闭包,闭包是什?它是怎么产生的及用来解决什么问题呢?先给出闭包的字面定义:闭包是由函数及其相关引用环境组合而成的实体(即:闭包=函数+引用环境).这个从字面上很难理解,特别对于一直使用命令式语言进行编程的程序员们. Go语言中的闭包 先看一个demo: func f(i int) func() int { return func() int { i++ return i } } 函数f返回了一个函数,返回的这个函数就是一个闭包.这个函数中本身是没有定义变量i的,而是引用

  • Swift语言中的函数学习教程

    函数是一个组织在一起语句集合,以执行特定任务.Swift 函数类似于简单 C 函数以及复杂的 Objective C 语言函数. 它使我们能够通过函数调用内部的局部和全局参数值. 像其他任何语言一样 swift 函数也遵循相同的步骤. 函数声明:它告诉编译器有关的函数的名称,返回类型和参数. 函数定义:它提供函数的实际主体. Swift 函数包含参数类型和返回类型. 函数定义 在Swift 语言中函数是由 "func" 关键字来定义.当一个新定义函数时,它可能需要一个或几个值作为函数输

  • 深入理解 Go 语言中的 Context

    Hi,大家好,我是明哥. 在自己学习 Golang 的这段时间里,我写了详细的学习笔记放在我的个人微信公众号 <Go编程时光>,对于 Go 语言,我也算是个初学者,因此写的东西应该会比较适合刚接触的同学,如果你也是刚学习 Go 语言,不防关注一下,一起学习,一起成长. 我的在线博客:http://golang.iswbm.com 我的 Github:github.com/iswbm/GolangCodingTime 1. 什么是 Context? 在 Go 1.7 版本之前,context 还

  • golang 语言中错误处理机制

    与其他主流语言如 Javascript.Java 和 Python 相比,Golang 的错误处理方式可能和这些你熟悉的语言有所不同.所以才有了这个想法根大家聊一聊 golang 的错误处理方式,以及实际开发中应该如何对错误进行处理.因为分享面对 Golang有一个基本的了解 developers, 所以一些简单地方就不做赘述了. 如何定义错误 在 golang 语言中,无论是在类型检查还是编译过程中,都是将错误看做值来对待,和 string 或者 integer 这些类型值并不差别.声明一个

  • 带你理解C语言中的汉诺塔公式

    目录 汉诺塔公式 汉诺塔问题在数学层面的公式: C语言递归公式 两层汉诺塔 三层汉诺塔 总结 汉诺塔公式 汉诺塔问题在数学层面的公式: 不用说,你看到这个公式一定一脸懵逼,我现在来讲解这个公式的作用. 先来回想一下大象放冰箱要几步,三步吧,打开冰箱,放进去,关上门就行了,我们先不要去思考一些细碎的步骤,将一个复杂的问题先简单化,再慢慢去分析. 那汉诺塔问题也是同样的简单三步:(假设有n个盘子) 一.把最大的盘子留在A柱,然后将其他的盘子全放在B柱. 二.把最大的盘子放到C柱. 三.然后将B柱上的

  • Swift语言中的一些访问控制设置详解

    限制访问代码块,模块和抽象通过访问控制来完成.类,结构和枚举可以根据自己的属性,方法,初始化函数和下标来通过访问控制机制进行访问.常量,变量和函数的协议限制,并允许通过访问控制来访问全局和局部变量.应用于属性,类型及函数的访问控制可以被称为"实体". 访问控制模型是基于模块和源文件的. 模块定义为代码分配一个单独的单元,并且可以使用import 关键字导入.源文件被定义为一个单一的源代码文件,模块可访问多种类型和函数. 三种不同的访问级别是由 Swift 语言提供.它们分别是 Publ

  • Go语言中的闭包详解

    一.函数的变量作用域和可见性 1.全局变量在main函数执行之前初始化,全局可见 2.局部变量在函数内部或者if.for等语句块有效,使用之后外部不可见 3.全局变量和局部变量同名的情况下,局部变量生效. 4.可见性: 包内任何变量或函数都是能访问的. 包外的话,首字母大写是可以访问的,首字母小写的表示私有的不能被外部调用. 二.匿名函数 1.Go语言中函数也是一种类型,所以可以用一个函数类型的变量进行接收. func anonyTest1(){ fmt.Println("anonyTest1&

  • 详解Swift语言中的类与结构体

    类 在 Swift 中类是建立灵活的构建块.类似于常量,变量和函数,用户可以定义的类的属性和方法.Swift给我们提供了声明类,而无需用户创建接口和实现文件的功能.Swift 允许我们创建类作为单个文件和外部接口,将默认在类一次初始化来创建. 使用类的好处: 继承获得一个类的属性到其他类 类型转换使用户能够在运行时检查类的类型 初始化器需要处理释放内存资源 引用计数允许类实例有一个以上的参考 类和结构的共同特征: 属性被定义为存储值 下标被定义为提供访问值 方法被初始化来改善功能 初始状态是由初

  • 深度理解C语言中的关键字static

    目录 一.函数和变量的多文件问题 1.1.为什么全局变量和函数需要跨文件访问 二.static修饰变量和函数 2.1.static修饰全局变量 2.2.static修饰局部变量 2.3.为什么局部变量具有临时性,全局变量具有全局性 总结 一.函数和变量的多文件问题 .h: 头文件,一般包含函数声明,变量声明,宏定义,头文件等内容(header) .c : 源文件,一般包含函数实现,变量定义等 (.c:c语言) 如果在一个源文件定义一个函数,然后再另一个源文件调用,这样的方式可行吗? 答案是可行的

随机推荐