太原模板建站系统,贵州网推传媒有限公司,成都怎样制作公司网站,马云做网站最早★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号#xff1a;山青咏芝#xff08;shanqingyongzhi#xff09;➤博客园地址#xff1a;山青咏芝#xff08;https://www.cnblogs.com/strengthen/#xff09;➤GitHub地址山青咏芝shanqingyongzhi➤博客园地址山青咏芝https://www.cnblogs.com/strengthen/➤GitHub地址https://github.com/strengthen/LeetCode➤原文地址https://www.cnblogs.com/strengthen/p/9728063.html ➤如果链接不是山青咏芝的博客园地址则可能是爬取作者的文章。➤原文已修改更新强烈建议点击原文地址阅读支持作者支持原创★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 闭包是自包含的功能块可以在代码中传递和使用。Swift中的闭包类似于C和Objective-C中的块以及其他编程语言中的lambdas。 闭包可以从定义它们的上下文中捕获和存储对任何常量和变量的引用。这被称为关闭那些常量和变量。Swift为您处理捕获的所有内存管理。 注意 如果您不熟悉捕获的概念请不要担心。下面在捕获值中详细解释了它。 全球和嵌套函数如推出的功能实际上是封闭的特殊情况。闭包采用以下三种形式之一 全局函数是具有名称但不捕获任何值的闭包。嵌套函数是具有名称的闭包可以从其封闭函数中捕获值。Closure表达式是一种未命名的闭包用轻量级语法编写可以从周围的上下文中捕获值。Swift的闭包表达式具有干净清晰的风格优化可以在常见场景中鼓励简洁无杂乱的语法。这些优化包括 从上下文中推断参数和返回值类型单表达式闭包的隐式返回速记参数名称尾随闭包语法 关闭表达式 嵌套函数在嵌套函数中引入是一种方便的方法可以将自包含的代码块命名和定义为更大函数的一部分。但是在没有完整声明和名称的情况下编写类似函数的构造的更短版本有时是有用的。当您使用将函数作为其一个或多个参数的函数或方法时尤其如此。 Closure表达式是一种以简短集中的语法编写内联闭包的方法。Closure表达式提供了几种语法优化用于以缩短的形式编写闭包而不会丢失清晰度或意图。下面的闭包表达式示例通过sorted(by:)在几次迭代中细化该方法的单个示例来说明这些优化每个迭代以更简洁的方式表达相同的功能。 排序方法 Swift的标准库提供了一个名为的方法sorted(by:)它根据您提供的排序闭包的输出对已知类型的值数组进行排序。完成排序过程后该sorted(by:)方法返回一个与旧数组相同类型和大小的新数组其元素按正确的排序顺序排列。该sorted(by:)方法不会修改原始数组。 下面的闭包表达式示例使用该sorted(by:)方法以String反向字母顺序对值数组进行排序。这是要排序的初始数组 let names [Chris, Alex, Ewa, Barry, Daniella] 该sorted(by:)方法接受一个闭包该闭包接受与数组内容相同类型的两个参数并返回一个Bool值以说明一旦值被排序后第一个值是出现在第二个值之前还是之后。true如果第一个值应出现在第二个值之前则需要返回排序闭包false否则返回。 这个例子是对String值数组进行排序因此排序闭包需要是类型的函数。(String, String) - Bool 提供排序闭包的一种方法是编写正确类型的普通函数并将其作为参数传递给sorted(by:)方法 func backward(_ s1: String, _ s2: String) - Bool { return s1 s2 }var reversedNames names.sorted(by: backward) // reversedNames is equal to [Ewa, Daniella, Chris, Barry, Alex] 如果第一个字符串s1大于第二个字符串s2则backward(_:_:)函数将返回true表示s1应该s2在排序数组之前出现。对于字符串中的字符“大于”表示“在字母表后面出现”。这意味着字母B“大于”字母A字符串Tom大于字符串Tim。这给出了一个反向字母排序Barry放在之前Alex依此类推。 然而这是一种相当冗长的方式来编写本质上是单表达式函数。在这个例子中最好使用闭包表达式语法内联编写排序闭包。a b 闭包表达式语法 Closure表达式语法具有以下一般形式 { (parameters) - return type in statements } 该参数在封闭表达式语法可以在输出参数但是他们不能有一个默认值。如果命名variadic参数则可以使用变量参数。元组也可以用作参数类型和返回类型。 下面的示例显示了backward(_:_:)上面函数的闭包表达式版本 reversedNames names.sorted(by: { (s1: String, s2: String) - Bool in return s1 s2 }) 请注意此内联闭包的参数声明和返回类型与backward(_:_:)函数声明相同。在这两种情况下它都写成。但是对于内联闭包表达式参数和返回类型写在花括号内而不是在花括号内。(s1: String, s2: String) - Bool 关闭的主体的开头由in关键字引入。这个关键字表示闭包的参数和返回类型的定义已经完成闭包的主体即将开始。 由于封盖的主体很短甚至可以写在一行上 reversedNames names.sorted(by: { (s1: String, s2: String) - Bool in return s1 s2 } ) 这说明对sorted(by:)方法的整体调用保持不变。一对括号仍然包装该方法的整个参数。但是该参数现在是内联闭包。 从上下文中推断类型 因为排序闭包作为参数传递给方法所以Swift可以推断出它的参数类型以及它返回的值的类型。该sorted(by:)方法是在一个字符串数组上调用的因此它的参数必须是一个类型的函数。这意味着不需要将和类型作为闭包表达式定义的一部分来编写。因为可以推断出所有类型所以也可以省略返回箭头和参数名称周围的括号(String, String) - Bool(String, String)Bool- reversedNames names.sorted(by: { s1, s2 in return s1 s2 } ) 在将闭包作为内联闭包表达式传递给函数或方法时始终可以推断出参数类型和返回类型。因此当闭包用作函数或方法参数时您永远不需要以最完整的形式编写内联闭包。 尽管如此如果您愿意仍然可以使类型显式化如果它避免了代码读者的歧义则鼓励这样做。在该sorted(by:)方法的情况下封闭的目的是从分类发生的事实中清楚的并且读者可以认为封闭可能与String值一起工作是安全的因为它有助于分类。一串字符串。 单表达式闭包的隐式返回 单表达式闭包可以通过return从声明中省略关键字来隐式返回单个表达式的结果如上一个示例的此版本中所示 reversedNames names.sorted(by: { s1, s2 in s1 s2 } ) 这里sorted(by:)方法参数的函数类型清楚地表明Bool闭包必须返回一个值。因为闭包的主体包含一个返回值的表达式所以没有歧义可以省略关键字。s1 s2Boolreturn 速记参数名称 雨燕自动提供速记参数名内联闭包它可以使用的名称指的是关闭的参数值$0$1$2等等。 如果在闭包表达式中使用这些简写参数名称则可以从其定义中省略闭包的参数列表并且将从期望的函数类型推断缩写参数名称的数量和类型。的in关键字也可以被省略因为封闭件表达是由完全其身体的 reversedNames names.sorted(by: { $0 $1 } ) 在这里$0并$1参考闭包的第一个和第二个String参数。 操作员方法 实际上有一种更短的方式来编写上面的闭包表达式。Swift的String类型将其大于运算符的字符串特定实现定义为具有两个类型参数的方法String并返回type的值Bool。这与方法所需的方法类型完全匹配sorted(by:)。因此您可以简单地传入大于运算符Swift将推断您要使用其特定于字符串的实现 reversedNames names.sorted(by: ) 欲了解更多有关操作方法请参阅操作方法。 尾随闭包 如果需要将闭包表达式作为函数的最终参数传递给函数并且闭包表达式很长则将其写为尾随闭包可能很有用。在函数调用的括号之后写入尾随闭包即使它仍然是函数的参数。使用尾随闭包语法时不要将闭包的参数标签写为函数调用的一部分。 func someFunctionThatTakesAClosure(closure: () - Void) { // function body goes here } // Heres how you call this function without using a trailing closure: someFunctionThatTakesAClosure(closure: { // closures body goes here }) // Heres how you call this function with a trailing closure instead: someFunctionThatTakesAClosure() { // trailing closures body goes here } 上面的Closure Expression Syntax部分中的字符串排序闭包可以sorted(by:)作为尾随闭包写在方法的括号之外 reversedNames names.sorted() { $0 $1 } 如果提供闭包表达式作为函数或方法的唯一参数并且您将该表达式作为尾随闭包提供则()在调用函数时不需要在函数或方法的名称后面写一对括号 reversedNames names.sorted { $0 $1 } 当闭包足够长以至于无法将其内联写入单行时尾随闭包最有用。作为一个例子Swift的Array类型有一个map(_:)方法它将一个闭包表达式作为它的单个参数。对数组中的每个项调用一次闭包并为该项返回一个替代映射值可能是某些其他类型。映射的性质和返回值的类型留给要指定的闭包。 将提供的闭包应用于每个数组元素后该map(_:)方法返回一个包含所有新映射值的新数组其顺序与原始数组中的相应值相同。 以下是如何使用map(_:)带尾随闭包的方法将Int值数组转换为值数组String。该数组用于创建新数组[16, 58, 510][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数组创建String值数组方法是将闭包表达式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 }// strings is inferred to be of type [String] // its value is [OneSix, FiveEight, FiveOneZero] 该map(_:)方法为数组中的每个项调用一次闭包表达式。您不需要指定闭包的输入参数number的类型因为可以从要映射的数组中的值推断出类型。 在此示例中number使用closure的number参数值初始化变量以便可以在闭包体内修改该值。函数和闭包的参数总是常量。闭包表达式还指定了返回类型String以指示将存储在映射的输出数组中的类型。 闭包表达式构建一个output每次调用时调用的字符串。它number使用余数运算符计算最后一位数并使用该数字在字典中查找相应的字符串。闭包可用于创建任何大于零的整数的字符串表示。number % 10digitNames 注意 对digitNames字典下标的调用后跟一个感叹号!因为字典下标返回一个可选值表示如果该键不存在字典查找可能会失败。在上面的示例中保证始终是字典的有效下标键因此使用感叹号强制解包存储在下标的可选返回值中的值。number % 10digitNamesString 从检索到的字符串digitNames辞典被添加到前面的output有效地建立反向一数目的字符串版本。表达式给出for for 和for的值。number % 106168580510 number然后将变量除以10。因为它是一个整数所以它在分割期间向下舍入因此16变为158变为5510变为51。 重复该过程直到number等于0此时output字符串由闭包返回并通过该map(_:)方法添加到输出数组。 在上面的示例中使用尾随闭包语法在闭包支持的函数之后立即巧妙地封装了闭包的功能而无需将整个闭包包装在map(_:)方法的外括号中。 捕捉价值观 闭包可以从定义它的周围上下文中捕获常量和变量。然后闭包可以引用并修改其体内的常量和变量的值即使定义常量和变量的原始范围不再存在。 在Swift中可以捕获值的最简单形式的闭包是嵌套函数写在另一个函数体内。嵌套函数可以捕获其外部函数的任何参数还可以捕获外部函数中定义的任何常量和变量。 这是一个调用函数的示例makeIncrementer其中包含一个名为的嵌套函数incrementer。嵌套incrementer()函数捕获两个值runningTotal并且amount从它的周围环境。捕获这些值后incrementer将makeIncrementer作为一个闭包返回runningTotalamount每次调用时它都会递增。 func makeIncrementer(forIncrement amount: Int) - () - Int { var runningTotal 0 func incrementer() - Int { runningTotal amount return runningTotal }return incrementer } 返回类型makeIncrementer是。这意味着它返回一个函数而不是一个简单的值。它返回的函数没有参数每次调用时都返回一个值。要了解函数如何返回其他函数请参见函数类型作为返回类型。() - IntInt 该makeIncrementer(forIncrement:)函数定义了一个名为的整数变量runningTotal用于存储将返回的增量器的当前运行总数。此变量初始化为值0。 该makeIncrementer(forIncrement:)函数有一个Int参数标签为forIncrement的参数参数名称为amount。传递给此参数的参数值指定runningTotal每次调用返回的增量函数时应递增多少。该makeIncrementer函数定义了一个名为的嵌套函数incrementer它执行实际的递增。此功能只是增加了amount对runningTotal并返回结果。 单独考虑时嵌套incrementer()函数可能看起来不常见 func incrementer() - Int { runningTotal amount return runningTotal } 该incrementer()函数没有任何参数但它在其函数体内引用runningTotal和引用amount。它通过捕获做到这一点参考以runningTotal和amount从周围的功能和其自身的函数体中使用它们。通过参考捕捉保证runningTotal和amount不消失的时候调用makeIncrementer结束而且也保证了runningTotal可用下一次incrementer函数被调用。 注意 作为优化如果该值未被闭包变异并且在创建闭包后该值未发生变化则Swift可以代之以捕获并存储值的副本。 Swift还处理在不再需要变量时处理变量所涉及的所有内存管理。 这是一个实际的例子makeIncrementer let incrementByTen makeIncrementer(forIncrement: 10) 此示例设置一个常量incrementByTen该常量调用以引用每次调用时添加10到其runningTotal变量的增量函数。多次调用该函数会显示此行为 incrementByTen() // returns a value of 10 incrementByTen() // returns a value of 20 incrementByTen() // returns a value of 30 如果您创建第二个增量器它将拥有自己存储的对新的单独runningTotal变量的引用 let incrementBySeven makeIncrementer(forIncrement: 7) incrementBySeven() // returns a value of 7 incrementByTen再次调用原始增量器会继续增加其自己的runningTotal变量并且不会影响由incrementBySeven以下内容捕获的变量 incrementByTen() // returns a value of 40 注意 如果为类实例的属性分配闭包并且闭包通过引用实例或其成员来捕获该实例则将在闭包和实例之间创建一个强引用循环。Swift使用捕获列表来打破这些强大的参考周期。有关更多信息请参阅闭包的强引用周期。 闭包是参考类型 在上面的例子中incrementBySeven并且incrementByTen是常量但这些常量引用的闭包仍然能够增加runningTotal它们捕获的变量。这是因为函数和闭包是引用类型。 无论何时将函数或闭包赋值给常量或变量实际上都是将该常量或变量设置为对函数或闭包的引用。在上面的例子中它是闭包的选择它incrementByTen 引用的是常量而不是闭包本身的内容。 这也意味着如果为两个不同的常量或变量分配闭包那么这两个常量或变量都引用相同的闭包。 let alsoIncrementByTen incrementByTen alsoIncrementByTen() // returns a value of 50 incrementByTen() // returns a value of 60 上面的例子显示调用alsoIncrementByTen与调用相同incrementByTen。因为它们都引用相同的闭包它们都会递增并返回相同的运行总计。 逃离闭包 闭包是说逃避当封盖作为参数传递给函数但在函数返回之后被调用的函数。当声明一个以闭包作为其参数之一的函数时可以escaping在参数的类型之前写入以指示允许闭包转义。 闭包可以转义的一种方法是存储在函数外部定义的变量中。作为示例许多启动异步操作的函数将闭包参数作为完成处理程序。函数在启动操作后返回但是在操作完成之前不会调用闭包 - 闭包需要转义以便稍后调用。例如 var completionHandlers: [() - Void] [] func someFunctionWithEscapingClosure(completionHandler: escaping () - Void) { completionHandlers.append(completionHandler) } 该someFunctionWithEscapingClosure(_:)函数将闭包作为其参数并将其添加到在函数外部声明的数组中。如果没有用此标记此函数的参数escaping则会出现编译时错误。 使用escaping意味着self在闭包中明确引用的方法标记闭包。例如在下面的代码中传递给的闭包someFunctionWithEscapingClosure(_:)是一个转义闭包这意味着它需要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) // Prints 200 completionHandlers.first?() print(instance.x) // Prints 100 Autoclosures 一个autoclosure是自动创建来包装被真实作为参数传递给函数的表达式的封闭件。它不接受任何参数当它被调用时它返回包含在其中的表达式的值。这种语法方便使您可以通过编写普通表达式而不是显式闭包来省略函数参数周围的大括号。 这是常见的来电称取autoclosures的功能但它不是常见的实现那种功能。例如该assert(condition:message:file:line:)函数为其condition和message参数采用autoclosure ; 它condition仅在调试参数进行评估并建立其message仅在参数评估condition是false。 autoclosure允许您延迟评估因为在您调用闭包之前内部代码不会运行。延迟评估对于具有副作用或计算成本高昂的代码非常有用因为它可以让您控制何时评估该代码。下面的代码显示了关闭延迟评估的方式。 var customersInLine [Chris, Alex, Ewa, Barry, Daniella] print(customersInLine.count) // Prints 5 let customerProvider { customersInLine.remove(at: 0) } print(customersInLine.count) // Prints 5 print(Now serving \(customerProvider())!) // Prints Now serving Chris! print(customersInLine.count) // Prints 4 即使customersInLine数组的第一个元素被闭包内的代码删除在实际调用闭包之前不会删除数组元素。如果从不调用闭包则永远不会计算闭包内的表达式这意味着永远不会删除数组元素。请注意类型customerProvider是不是String但不带任何参数返回一个字符串-a功能。() - String 当您将闭包作为参数传递给函数时您会得到与延迟求值相同的行为。 // customersInLine is [Alex, Ewa, Barry, Daniella] func serve(customer customerProvider: () - String) { print(Now serving \(customerProvider())!) }serve(customer: { customersInLine.remove(at: 0) } ) // Prints Now serving Alex! serve(customer:)上面列表中的函数采用显式闭包返回客户的名称。下面的版本serve(customer:)执行相同的操作但不是采用显式闭包而是通过使用autoclosure属性标记其参数的类型来进行自动闭包。现在你可以调用该函数就好像它使用了一个String参数而不是一个闭包。参数自动转换为闭包因为customerProvider参数的类型用autoclosure属性标记。 // customersInLine is [Ewa, Barry, Daniella] func serve(customer customerProvider: autoclosure () - String) { print(Now serving \(customerProvider())!) }serve(customer: customersInLine.remove(at: 0)) // Prints Now serving Ewa! 注意 过度使用autoclosures会使您的代码难以理解。上下文和函数名称应该明确表示正在推迟评估。 如果您想要允许转义的autoclosure请使用autoclosure和escaping属性。escaping上面的Escaping Closures中描述了该属性。 // customersInLine is [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.) // Prints Collected 2 closures. for customerProvider in customerProviders { print(Now serving \(customerProvider())!) }// Prints Now serving Barry! // Prints Now serving Daniella! 在上面的代码中函数将闭包附加到数组而不是调用作为customerProvider参数传递给它collectCustomerProviders(_:)的闭包customerProviders。数组声明在函数范围之外这意味着在函数返回后可以执行数组中的闭包。因此customerProvider必须允许参数的值转义函数的作用域。 转载于:https://www.cnblogs.com/strengthen/p/9728063.html