深入理解Swift语言中的闭包机制_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

 
 
 

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索swift
闭包
深入理解闭包、深入理解js原型和闭包、swift 闭包、swift 3.0 闭包、swift 闭包传值,以便于您获取更多的相关知识。

时间: 2024-08-02 14:53:05

深入理解Swift语言中的闭包机制_Swift的相关文章

Swift语言中如何使用JSON数据教程

原文:Swift语言中如何使用JSON数据教程 这是一篇翻译文章,原文出处:http://www.raywenderlich.com/82706/working-with-json-in-swift-tutorial   Swift语言中如何使用JSON数据教程   JSON(全称:JavaScript Object Notation),是网络服务中传输数据的常用方法,JSON因为容易使用,且可读性强, 所以非常受到欢迎.   下面是个JSON的一个片段: [ {"person": {

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

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

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

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

LLVM提议向C语言中加入模块机制

作者 Alex Blewitt ,译者 臧秀涛 发布于 十二月 05, 2012 在今年11月的LLVM开发者大会上,来自Apple的Doug Gregor做了一场讲座,主题是向C语言中加入模块(Module)机制.讲座中提到: 长期以来,C的预处理器就是程序员和工具的问题之源.写得不好的头文件致使宏污染和包含顺序等问题大量存在,程序员必须不断地与之斗争.为了缓解这些问题,开发者习惯上采用各种预处理器变通方案,比如LONG_MACRO_PREFIXES这种风格的很长的宏前缀,#include防卫

深入解析Swift语言中的协议_Swift

协议为方法,属性和其他要求的功能提供了一个蓝本.它只是描述了方法或属性的骨架,而不是实现.方法和属性实现还可以通过定义类,函数和枚举完成.协议的一致性是指方法或属性满足协议的要求. 语法协议也遵循类似类,结构和枚举的语法: 复制代码 代码如下: protocol SomeProtocol {     // protocol definition } 协议在类,结构或枚举类型命名声明.单个和多个协议的声明也是可以的.如果多个协议规定,它们必须用逗号分隔. 复制代码 代码如下: struct Som

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

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

简单分析Swift语言的一些基本特征_Swift

Swift是苹果公司最新推出的编程语言,据很多人说,是用来"代替"Objective-C.但是没有确切的证据.我花了一些时间对Swift二进制和运行环境实施逆向工程技术,然后我对Swift有些少许的发现.目前为止,结论就是:Swift是没有消息机制的Objective-C. 对象 信不信由你,Swift中的对象就是Objective-C的对象.在Mach-O二进制文件中,__objc_classlist包含每个二进制文件中类的数据.其结构如下所示:   复制代码 代码如下: struc

详解Swift语言的while循环结构_Swift

Swift 编程语言中的 while 循环语句只要给定的条件为真时,重复执行一个目标语句. 语法 Swift 编程语言的 while 循环的语法是: 复制代码 代码如下: while condition {    statement(s) } 这里 statement(s) 可以是单个语句或语句块.condition 可以是任何表达式.循环迭代当条件(condition)是真的. 当条件为假,则程序控制进到紧接在循环之后的行. 数字0,字符串"0"和"",空列表 l

结合实例实习F#(三)--理解函数式语言中的函数

前两篇我主要说了些F#中基本的语法,今天我接着来说说函数,函数在函数式编程中起着非常重要的作用,可以夸张一点来说,如果你了解并能熟练应用函数,你就可以向别人说"我精通函数式编程了". 经常有人觉得F#难懂难用,我觉得一部分原因是F#中的函数接口(这里的接口指的是function signature, 我习惯叫它函数接口,如果对您阅读带来什么不便,请见谅). 看起来和我们平常熟悉的很不一样(比如C#),导致一些朋友在尝鲜阶段遇到困难,进而觉得其难懂难用,最后彻底将F#打入冷宫.希望下面的