功能性Swift:闭包{}
闭包是可以独立传递的功能块,可以在代码中传递和使用。
– 苹果
闭包可以从定义它们的上下文中捕获和存储对任何常量和变量的引用,因此称为闭包。 您可以将闭包视为一个没有自己名称的函数,并从其环境中捕获任何值。 函数和闭包是Swift中的一流对象 :您可以存储它们,将它们作为函数的参数传递,并像对待其他任何值或对象一样对待它们。 将闭包作为完成处理程序传递是许多API中的常见模式。 标准Swift库主要将闭包用于事件处理和回调。
函数是执行特定任务的独立代码块。 您为函数指定一个名称,该名称可以标识其功能,该名称用于“调用”该函数以在需要时执行其任务。 您可以使用func
关键字定义一个函数。 函数可以不带太多参数,可变参数而返回任何一个或多个参数。
函数类型由参数类型和函数的返回类型组成。 对于上面的示例,函数类型为: (Int, Int) -> Int
可以将其理解为:“具有两个参数的函数,都具有Int
类型,并且都返回Int
类型的值。”可以将函数类型设置为参数或函数的返回类型。
可以将函数类型分配给任何这样的变量:
var mathFunction:(Int,Int)-> Int =添加
函数是闭包的特殊情况。 闭包采用以下三种形式之一:
- 全局函数:它们具有名称,不能捕获值。
- 嵌套函数:它们有一个名称,可以从其封闭函数中捕获值。
- 闭包表达式:它们没有名称,可以从其周围的上下文中捕获值。
可以通过将函数类型放在大括号内并in
返回类型后in
关键字中来创建闭包。
单表达式闭包可以通过从声明中省略return
关键字来隐式返回其单表达式的结果。
对于多行表达式闭包,不能省略return
关键字。
如果由于函数的最后一个参数而需要将闭包表达式传递给函数并且闭包表达式太长,则可以将其写为尾随闭包。 在函数调用的括号()后面写一个结尾的闭包,即使它仍然是函数的参数。 使用尾随闭包语法时,不要在函数调用的过程中为闭包编写参数标签。
如果闭包是方法的最后一个参数,那么swift允许您这样编写:
尾随闭包语法的使用可以在闭包支持的功能之后立即整齐地封装闭包的功能,而无需将整个闭包包装在reduce(_:)
方法的外部括号内。
闭包可以从定义它的周围上下文中捕获常量和变量。 然后,闭包可以从其主体内部引用和修改这些常量和变量的值,即使定义常量和变量的原始范围不再存在。
在Swift中,最简单的可以捕获值的闭包形式是嵌套函数,它写在另一个函数的主体内。 嵌套函数可以捕获其外部函数的任何自变量,还可以捕获在外部函数内定义的任何常量和变量。
此makeIncrementer
函数接受一个参数(即Int)作为输入,并返回一个函数类型,即() -> Int
。 这意味着它将返回一个function ,而不是一个简单的值。 它返回的函数没有参数,并且每次调用都返回一个Int
值。
在这里, amount
是参数, runningTotal
被声明为变量并初始化为0。嵌套函数incrementer
runningTotal
从周围的上下文中捕获amount
和runningTotal
。
让我们看看makeIncrementer
的作用:
注意:作为一种优化,Swift可以捕获并存储值的副本 ,如果该值未由闭包更改,并且该值在创建闭包后也未更改。
当不再需要变量处理时,Swift还可以处理所有与变量处理有关的内存管理。
要摆脱函数参数中的长闭包表达式,可以使用typealias。
在Swift 3之前,默认情况下,闭包参数是转义的。如果将闭包参数标记为非转义,则闭包将无法转义函数体
在Swift 3中,它已被逆转。 当您将闭包作为函数参数传递时,闭包将使用函数的主体执行并返回编译器。 执行结束时,传递的闭包超出范围,并且在内存中不再存在。
默认情况下,闭包参数是不转义的,如果要转义闭包执行,则必须对闭包参数使用@escaping 。
非转义闭包的生命周期:
1.在函数调用期间,将闭包作为函数参数传递。
2.在函数中做一些工作,然后执行关闭。
3.函数返回。
由于更好的内存管理和优化,Swift将所有闭包默认更改为不转义。 CaptureList.swift
是非转义闭包的示例。
注意:@ non-escapeing注释仅适用于函数类型
当闭包作为函数的参数传递给闭包时,闭包被认为是对函数的转义 ,但是在函数返回后会被调用。 用@escaping
标记闭包意味着您必须在闭包内显式引用self
。
@escaping闭包的生命周期:
1.在函数调用期间,将闭包作为函数参数传递。
2.在功能上做一些额外的工作。
3.函数异步执行闭包或存储闭包。
4.函数返回。
让我们看看默认情况下闭包在哪里转义:
- 函数类型的变量是隐式转义
- 类型别名是隐式转义
- 可选的闭包是隐式转义
常见错误:
将非转义闭包分配给转义闭包。 有两种方法可以解决此问题:
- 将关闭标记为逃逸
- 或者通过使闭包为可选来保留默认的@noescape行为
Swift的@autoclosure
属性使您可以定义一个自动包装在闭包中的参数。 它不带任何参数,并且在调用它时,它返回包装在其中的表达式的值。 这种语法上的便利性使您可以通过编写正则表达式而不是显式闭包来省略函数参数的花括号。
例如, assert(condition:message:file:line:)
函数会自动关闭其condition
和message
参数; 仅在调试版本中评估其condition
参数,并且仅在condition
为false
评估其message
参数。
func assert(_ expression:@autoclosure()->布尔,
_消息:@autoclosure()->字符串){}
要将@autoclosure
与@escaping
属性语法一起使用,请@escaping
操作:
@autoclosure @escaping () -> Bool
“ Swift闭包和Objective-C块兼容,因此您可以将Swift闭包传递给需要块的Objective-C方法。 Swift闭包和函数具有相同的类型,因此您甚至可以传递Swift函数的名称。 闭包具有与块相似的捕获语义,但在一个关键方面有所不同:变量是可变的,而不是复制的。 换句话说,Objective-C中__block的行为是Swift中变量的默认行为。”
解决方案取决于问题。 此外,苹果正在将其重点转移到回调模式。 UIAlertAction
就是一个例子。
https://medium.com/@abhimuralidharan/functional-swift-all-about-closures-310bc8af31dd
https://medium.com/@kumarpramod017/what-do-mean-escaping-and-nonescaping-closures-in-swift-d404d721f39d
https://oleb.net/blog/2016/10/optional-non-escaping-closures/
https://swiftunboxed.com/lang/closures-escaping-noescape-swift3/
苹果/迅捷进化
swift-evolution –保留了对Swift编程语言进行更改和用户可见的增强的建议。
github.com
https://medium.com/@johnsundell/using-autoclosure-when-designing-swift-apis-67fe20a8b2e
在我的下一篇文章中查看Curry Function。
有时我们使用概念,但不了解术语。 这篇文章对新手和有经验的开发人员都将是有用的。 我@escaping
使用@non-escaping
@escaping
, @non-escaping
和@autoclosure
,但并不了解实际概念。 我想深入研究并与大家分享。
您可以在以下位置找到我:
Linkedin: Aaina Jain
推特: __aainajain
如果您对下一篇文章有任何建议,请给我发送电子邮件至aainajain100@gmail.com。