Swift 中的Closures 閉包 詳解

2022-05-05 01:51:09 字數 3888 閱讀 6145

在swift沒有發布之前,所有人使用oc語言編寫cocoa上的程式,而其中經常被人們討論的其中之一 -- block 一直備受大家的喜愛。在swift中,同樣有這樣的乙個角色,用於當開發者需要非同步執行的之後使用的一種語法 - closure。中文翻譯為閉包。

本文介紹幾種閉包的形式,以及一些閉包的特性。

這是乙個最基本的閉包的形式:

閉包中,包含三要素: 引數,返回型別,閉包體。  其中引數和返回型別可以忽略, 但是乙個閉包體必需存在,實際上就算在閉包體裡面,什麼都不寫,閉包體本身還是以不執行任何**的形式存在。

reversednames = names.sorted(by: )

這是乙個高階函式,同時,也是乙個閉包的基本使用。包含了 引數及型別,返回值型別,閉包體。

我們可以簡寫閉包的形式,採用內聯的方式書寫:

reversednames = names.sorted(by:  )

在閉包中,因為包含了上下文的變數和常量的引用,並做了型別推斷,所以,實際上,對於閉包的引數來說,型別是固定的,當然返回的型別也是固定的,swift允許開發者書寫時省略。就像下面這樣:

reversednames = names.sorted(by:  )

如果閉包是的閉包體是單個表示式(只有一條執行語句)的時候,甚至可以將return都省略

reversednames = names.sorted(by:  )

另外swift的閉包又乙個特性:swift自動為內聯閉包提供簡寫引數名稱,可用於通過名稱$ 0,$ 1,$ 2等引用閉包引數的值。

如果在閉包表示式中使用這些簡寫引數名稱,則可以從其定義中省略閉包的引數列表,並根據預期的函式型別推斷速記引數名稱的數量和型別。 in關鍵字也可以省略,因為閉包表示式完全由其主體組成:

reversednames = names.sorted(by:  )

這樣還不夠完美,如果將」 > 「符號過載,使  $0 > $1 直接用 > 代替是不是更加簡潔呢? 當然可以,事實上,swift中的 > 已經被定義了 > 指代兩個swift string之間的比較,並返回乙個 bool值。那麼我們的最終版本應該是這樣的: 

reversednames = names.sorted(by: >)

如果你定義了乙個閉包,將之作為乙個函式的最後的乙個引數,並且,由於之前還包含了其他的引數,導致這個函式變得很長,你可以使用一種簡短的方式書寫閉包。像下面這樣的函式:

func somefunctionthattakesaclosure(closure: () -> void)

如果我們呼叫的使用,應該是這樣的:

somefunctionthattakesaclosure(closure: )

像這樣的情況,我們考慮它構成了尾隨閉包,對於尾隨閉包,有另一中更加簡便的方式書寫:

somefunctionthattakesaclosure()

注意,這時候,閉包的標籤   closure  應該被省略。

這樣的情況,我們可以寫出閉包的最終版本的特別版本:  

reversednames = names.sorted(>)

尾隨閉包使用在乙個長度很長的閉包下,效果更好。你可以使用這個技巧寫出很簡短優雅的**。 

之前其實已經介紹了,閉包的本質類似於乙個巢狀函式,它具有和巢狀函式一樣能力:獲取區域性的變數和常量,在乙個閉包中:它可以獲取到外部函式所有引數,和外部函式內定義的任何的常量和變數。

這裡有個巢狀的函式,他和閉包的作用一樣:

func makeincrementer(forincrement amount: int) -> () -> int 

return incrementer

}

incrementer 可以獲取到amount和runingtotal的值來使用。

呼叫:

let incrementbyten = makeincrementer(forincrement: 10)    //繫結引用, 保證用完不會消失。
incrementbyten()

// returns a value of 10

incrementbyten()

// returns a value of 20

incrementbyten()

// returns a value of 30

incrementer 這個巢狀的函式一值在改變runningtotal的值,同時接收不斷變化的amount。

如果換乙個函式變數引用,那麼值則會被重新整理:

let incrementbyseven = makeincrementer(forincrement: 7)

incrementbyseven()

// returns a value of 7

閉包的引用具有關聯性。和一般的變數一樣:

let alsoincrementbyten = incrementbyten

alsoincrementbyten()

// returns a value of 50

在網路請求的過程中,大部分時候都是非同步操作的,一般的閉包,雖然已經定義了閉包的閉包體(閉包實現),但是在請求**的時候,閉包可能不會被呼叫。原因是可能被釋放, 或者可能被修改。

如果需要保證做到非同步的呼叫,那麼需要在閉包的引數前加 @escaping標記。

新增@escaping標記的閉包,意味著,如果需要訪問自身的自身的變數的時候,必須在閉包內包含自身的引用(或者自身的屬性、變數等)。

下面是兩種是否帶標籤的閉包的對比:

var completionhandlers: [() -> void] = 

func somefunctionwithescapingclosure(completionhandler: @escaping () -> void)

func somefunctionwithnonescapingclosure(closure: () -> void)

兩者呼叫:

class someclass 

somefunctionwithnonescapingclosure

}}

預設的閉包都是 非逃逸閉包。

自動閉包的意思是當閉包做為函式的引數的時候,對於閉包的內容執行的時機取決於誰。  普通的閉包,將會在讀取閉包的時候執行,而自動閉包,在讀取的時候,將閉包做為其引數型別處理,只有等到執行閉包的時候才會處理閉包內的內容。

//

customersinline is ["ewa", "barry", "daniella"]

func serve(customer customerprovider: @autoclosure () ->string)

serve(customer: customersinline.remove(at: 0))

//prints "now serving ewa!"

在執行之前,customerprovider被當作乙個 () -> string 處理,而不會涉及到閉包體內部的處理。 如果不使用@autoclosure,那麼函式處理閉包體內的內容。

日常的開發可以使用:

public func dlog( item:@autoclosure ()->any)

預設的自動閉包是 非逃逸閉包,如果想變成逃逸閉包,可以如下:

func collectcustomerproviders(_ customerprovider: @autoclosure @escaping () ->string)

Swift 學習之閉包 Closures

swift 學習之閉包 closures 閉包 closures 1.閉包概念 閉包是功能性自包含模組,可以在 中被傳遞和使用。swift 中的閉包與 c 和 objective c 中的 blocks 以及其他一些程式語言中的 lambdas 比較相似。閉包可以捕獲和儲存其所在上下文中任意常量和變...

Swift學習之閉包Closures

原始碼位址 let learnios learnios 引數lan,in將引數與函式體隔開 let learn learn swift return為一行時可省了return let learn1 let result learn1 swiftui print result func findwor...

iOS 中 Block 和 Closures 簡介

目前ios開發有兩種語言,objective c 和swift。在objective c中,block的使用非常頻繁,在開發中佔據了很重要的位置。closures,通常被稱作閉包,同樣是swift語言中很重要的乙個部分。其功能類似objective c中的block。先回顧一下block的用法 通常...