什么是閉包
維基百科中的解釋:
- 在計算機科學(xué)中,閉包(
Closure
),又稱詞法閉包(Lexical Closure
)或函數(shù)閉包(function closures
),是在支持頭等函數(shù)的編程語言中實現(xiàn)詞法綁定的一種技術(shù)。- 閉包在實現(xiàn)上是一個結(jié)構(gòu)體,它存儲了一個函數(shù)(通常是其入口地址)和一個關(guān)聯(lián)的環(huán)境(相當(dāng)于一個符號查找表)。
func test() {
print("test")
}
上述代碼,
test
是?個全局函數(shù),也是?種特殊的閉包,只不過當(dāng)前全局函數(shù)并不捕獲值
func makeIncrementer() -> () -> Int {
var runningTotal = 12
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
上述代碼,
incrementer
稱之為內(nèi)嵌函數(shù),同時從上層函數(shù)makeIncrementer
中捕獲變量runningTotal
{(age: Int) -> Int in
return age
}
上述代碼,稱之為閉包表達式,是?個匿名函數(shù),可以從上下?中捕獲變量和常量
閉包表達式
閉包表達式是
Swift
語法,可以更簡潔的傳達信息,具有以下特性:
- 利?上下?推斷參數(shù)和返回值類型
- 單表達式可以隱?返回,既省略
return
關(guān)鍵字- 參數(shù)名稱可簡寫,?如
$0
- 尾隨閉包表達式
閉包表達式書寫格式:
{(param) -> ReturnType in //函數(shù)體 }
- 作?域,也就是?括號
- 參數(shù)和返回值
- 函數(shù)體,也就是
in
之后的代碼
Swift
中閉包即可以當(dāng)做變量,也可以當(dāng)做參數(shù)傳遞var closure: (Int) -> Int = {(age: Int) in return age } func test(param : (Int) -> Int){ print(param(10)) } test(param: closure)
可以將閉包聲明?個可選類型,需要在參數(shù)和返回值外整體加
()?
//錯誤寫法 //var closure: (Int) -> Int? //closure = nil //正確寫法 var closure: ((Int) -> Int)? closure = nil
注釋中的錯誤寫法,相當(dāng)于僅返回值是可選類型
可以通過
let
關(guān)鍵字將閉包聲明為?個常量,?旦賦值后不可改變let closure: (Int) -> Int closure = {(age: Int) in return age }
尾隨閉包
把閉包表達式作為函數(shù)的最后?個參數(shù)。如果當(dāng)前閉包表達式很?,可以通過尾隨閉包的書寫?式來提?代碼的可讀性
func test(_ a: Int, _ b: Int, _ c: Int, by: (_ item1: Int, _ item2: Int, _ item3: Int) -> Bool) -> Bool{
return by(a, b, c)
}
//常規(guī)寫法
test(10, 20, 30, by: {(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
return (item1 + item2 < item3)
})
//尾隨閉包寫法
test(10, 20, 30){(_ item1: Int, _ item2: Int, _ item3: Int) -> Bool in
return (item1 + item2 < item3)
}
尾隨閉包寫法,?眼看上去就知道是函數(shù)的調(diào)?。后?是?個閉包表達式,
{}
放在函數(shù)外?
var array = [1, 2, 3]
//常規(guī)寫法
array.sort{(item1 : Int, item2: Int) -> Bool in return item1 < item2 }
//省略參數(shù)類型,根據(jù)上下文自動推斷
array.sort(by: {(item1, item2) -> Bool in return item1 < item2 })
//省略返回值類型,根據(jù)上下文自動推斷
array.sort(by: {(item1, item2) in return item1 < item2 })
//單表達式可省略return關(guān)鍵字
array.sort{(item1, item2) in item1 < item2 }
//參數(shù)簡寫,通過$0、$1、$2...按順序替代對應(yīng)位置的參數(shù)
array.sort{ return $0 < $1 }
//單表達式可省略return關(guān)鍵字
array.sort{ $0 < $1 }
//使用高階函數(shù),指定排序規(guī)則
array.sort(by: <)
上述代碼,展示了閉包表達式的好處:
- 使用尾隨閉包,
{}
放在函數(shù)外?,提?可讀性- 利?上下?推斷參數(shù)和返回值類型
- 單表達式可以隱?返回,可省略
return
關(guān)鍵字- 參數(shù)簡寫,通過
$0、$1、$2...
按順序替代對應(yīng)位置的參數(shù)- 最后的代碼并不是尾隨閉包的寫法,使用高階函數(shù)指定排序規(guī)則,可以只傳入
<
,這種方式by:
不能省略
捕獲值
func makeIncrementer() -> () -> Int {
var runningTotal = 10
func incrementer() -> Int {
runningTotal += 1
return runningTotal
}
return incrementer
}
print("makeIncrementer:\(makeIncrementer()())")
print("makeIncrementer:\(makeIncrementer()())")
print("makeIncrementer:\(makeIncrementer()())")
print("--------------------")
let makeInc = makeIncrementer()
print("makeInc:\(makeInc())")
print("makeInc:\(makeInc())")
print("makeInc:\(makeInc())")
//輸出以下內(nèi)容:
//makeIncrementer:11
//makeIncrementer:11
//makeIncrementer:11
//--------------------
//makeInc:11
//makeInc:12
//makeInc:13
上述代碼中,使用
makeIncrementer()()
調(diào)用,每次都重新分配runningTotal
變量并進行+1
,所以輸出結(jié)果都是11
。但使用makeInc()
調(diào)用,內(nèi)嵌函數(shù)incrementer
內(nèi)部捕獲了runningTotal
變量,所以輸出結(jié)果是11、12、13
通過SIL代碼,分析
makeIncrementer
函數(shù)是如何捕獲變量的
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
makeIncrementer
alloc_box
官方文檔說明:在堆上分配一塊內(nèi)存空間,存儲了metadata
、refCount
、當(dāng)前的value
alloc_box通過斷點查看匯編代碼,確實調(diào)?了
swift_allocObject
?法
匯編代碼
- 捕獲變量的本質(zhì):就是在堆上開辟內(nèi)存空間,將當(dāng)前的變量存儲到里面
- 閉包的本質(zhì):就是當(dāng)前的內(nèi)嵌函數(shù),加上捕獲的變量或者常量
- ?個閉包能夠從上下?捕獲已被定義的常量和變量。即使定義這些常量和變量的原作?域已經(jīng)不存在, 閉包仍能夠在其函數(shù)體內(nèi)引?和修改這些值
- 每次修改捕獲值的時候,修改的是堆區(qū)中的
value
值- 每次重新執(zhí)?當(dāng)前函數(shù)的時候,都會重新創(chuàng)建內(nèi)存空間
閉包是引?類型
makeIncrementer
函數(shù)賦值給變量,這時變量??存儲的是什么?是函數(shù)地址嗎?
這?通過斷點查看匯編代碼,無法看出
makeInc
變量存儲的是什么
匯編代碼這時可以把
SIL
代碼再降?級,通過IR
來觀察數(shù)據(jù)的構(gòu)成
IR語法
- 數(shù)組
[<elementnumber> x <elementtype>] //example alloca [24 x i8], align 8 //24個i8都是0
- 結(jié)構(gòu)體
%swift.refcounted = type { %swift.type*, i64 } //表示形式 %T = type {<type list>} //這種和C語?的結(jié)構(gòu)體類似
- 指針類型
<type> * //example i64* //64位的整形
getelementptr
指令
LLVM
中獲取數(shù)組和結(jié)構(gòu)體的成員,通過getelementptr
指令:<result> = getelementptr <ty>, <ty>* <ptrval>{, [inrange] <ty> <id x>}* <result> = getelementptr inbounds <ty>, <ty>* <ptrval>{, [inrange] <ty> <idx>}*
創(chuàng)建
test.c
文件,運行一個來自LLVM
官?的getelementptr
指令的案例struct munger_struct { int f1; int f2; }; void munge(struct munger_struct *P) { P[0].f1 = P[1].f1 + P[2].f2; } struct munger_struct array[3]; //int main(int argc, const char * argv[]) { // // munge(array); // return 0; //}
將上述代碼生成IR文件:
clang -S -fobjc-arc -emit-llvm test.c
IR結(jié)合以下代碼理解?下
int array[4] = {1, 2, 3, 4}; int a = array[0];
其中
int a = array[0]
這句對應(yīng)LLVM
代碼應(yīng)該是這樣的:a = getelementptr inbounds [4 x i32], [4 x i32]* array, i64 0, i6 4 0
結(jié)合下面這張圖,可以看到第?個
0
,使?基本類型[4 * i32]
,因此返回的指針前進0 * 16字節(jié)
,也就是當(dāng)前數(shù)組的?地址。第?個index
,使?的基本類型是i32
,返回的指針前進0字節(jié)
,也就是當(dāng)前數(shù)組的第?個元素。返回的指針類型為i32 *
- 第?個索引不會改變返回指針的類型,也就是說
ptrval
前?的*
對應(yīng)什么類型,就返回什么類型- 第?個索引的偏移量是由第?個索引的值和第?個
ty
指定的基本類型共同確定的- 后?的索引是在數(shù)組或結(jié)構(gòu)體內(nèi)進?索引
- 每增加?個索引,就會使該索引使?的基本類型和返回指針的類型去掉?層
GEP
作?于結(jié)構(gòu)體時,其索引?定要是常量。GEP
指令只是返回?個偏移后的指針,并沒有訪問內(nèi)存
了解IR語法后,回到
makeIncrementer
案例,通過IR代碼,查看makeInc
變量存儲的是什么
將上述代碼生成IR文件:
swiftc -emit-ir main.swift | xcrun swift-demangle
swift.refcounted找到
main.makeIncrementer()
函數(shù)
makeIncrementer
bitcast
在進?指針類型轉(zhuǎn)換的過程中,sourceType
的位??和destType
必須相同。如果源類型是指針,則?標類型也必須是相同??的指針,轉(zhuǎn)換就好像該值已存儲到內(nèi)存中并作為destType
類型讀取?樣。類似Swift
中的unsafeBitCast
定義
FuntionData
結(jié)構(gòu)體,仿照IR
的swift.function
進行代碼還原struct FuntionData<T>{ var ptr: UnsafeRawPointer var captureValue: UnsafePointer<T> }
ptr
:內(nèi)嵌函數(shù)地址captureValue
:捕獲值地址
定義
HeapObject
結(jié)構(gòu)體,相當(dāng)于swift.refcounted
struct HeapObject{ var type: UnsafeRawPointer var refCount1: UInt32 var refCount2: UInt32 }
定義
Box
結(jié)構(gòu)體,相當(dāng)于swift.full_boxmetadata
struct Box<T> { var refCounted: HeapObject var value: T }
refCounted
:HeapObjectvalue
:捕獲值
定義完所有結(jié)構(gòu)體,在使用過程中,
makeInc
無法直接綁定為FuntionData<Box<Int>>
類型,因為編譯器無法推斷出具體類型
編譯報錯定義
VoidIntFun
結(jié)構(gòu)體,用結(jié)構(gòu)體將函數(shù)包裹一層,并將函數(shù)設(shè)為結(jié)構(gòu)體的第一個屬性,目的是利用結(jié)構(gòu)體地址就是首元素地址的特性struct VoidIntFun { var f: () ->Int }
聲明
makeInc
結(jié)構(gòu)體,傳入makeIncrementer
函數(shù),獲取VoidIntFunc
類型指針ptr
,再將ptr
綁定為FunctionData<Box<Int>>
類型,最終返回指針var makeInc = VoidIntFun(f: makeIncrementer()) let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1) ptr.initialize(to: makeInc) let ctx = ptr.withMemoryRebound(to: FuntionData<Box<Int>>.self, capacity: >1) { $0.pointee } print("incrementer內(nèi)嵌函數(shù)地址:\(ctx.ptr)") print("runningTotal值:\(ctx.captureValue.pointee.value)") //輸出以下內(nèi)容: //incrementer內(nèi)嵌函數(shù)地址:0x0000000100005800 //runningTotal值:10
搜索符號:
nm
【Mach-O
路徑】| grep
【地址】。在終端搜索地址0000000100005800
,記住沒有前面的0x
nm /Users/x/Library/Developer/Xcode/DerivedData/LGSwiftTest-ditbotpgxmdgkigjpenpcpgbyiwt/Build/Products/Debug/LGSwiftTest | grep 0000000100005800
輸出
incrementer
內(nèi)嵌函數(shù)的符號0000000100005800 t _$s11LGSwiftTest15makeIncrementerSiycyF11incrementerL_SiyFTA
還原符號名稱:
xcrun swift-demangle
【符號】,在終端還原符號名稱,記住沒有前面的_$
xcrun swift-demangle s11LGSwiftTest15makeIncrementerSiycyF11incrementerL_SiyFTA
輸出
incrementer
內(nèi)嵌函數(shù)$s11LGSwiftTest15makeIncrementerSiycyF11incrementerL_SiyFTA ---> partial apply forwarder for incrementer #1 () -> Swift.Int in LGSwiftTest.makeIncrementer() -> () -> Swift.Int
如果捕獲的是多個值,內(nèi)存布局是什么樣子的?
修改案例,讓makeIncrementer
函數(shù)接收一個Int
類型參數(shù)func makeIncrementer(forIncrement amount: Int) -> () -> Int { var runningTotal = 5 func incrementer() -> Int { runningTotal += amount return runningTotal } return incrementer }
將上述代碼生成IR文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
IR通過
lldb
查看內(nèi)存
lldb
案例:捕獲多個值
捕獲
makeIncrementer
函數(shù)的入?yún)?code>amount,以及函數(shù)內(nèi)部的runningTotal
、val
、str
三個不同數(shù)據(jù)類型變量
struct HeapObject{
var type: UnsafeRawPointer
var refCount1: UInt32
var refCount2: UInt32
}
struct FuntionData<T>{
var ptr: UnsafeRawPointer
var captureValue: UnsafePointer<T>
}
struct Box<T1,T2,T3,T4> {
var refCounted: HeapObject
var valueBox: UnsafePointer<ValueBox<T1,T2,T3>>
var value: T4
}
struct ValueBox<T1,T2,T3> {
var obj1: ValueBoxObj<T1>
var obj2: ValueBoxObj<T2>
var obj3: ValueBoxObj<T3>
}
struct ValueBoxObj<T> {
var refCounted: HeapObject
var value: T
var type: UnsafeRawPointer
}
struct VoidIntFun {
var f: () ->Int
}
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal: Int = 3
var val: Double = 5.5
var str: String = "Zang"
func incrementer() -> Int {
runningTotal += amount
val += Double(amount)
str += String(amount)
return runningTotal
}
return incrementer
}
var makeInc = VoidIntFun(f: makeIncrementer(forIncrement: 10))
let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
ptr.initialize(to: makeInc)
let ctx = ptr.withMemoryRebound(to: FuntionData<Box<Int,Double,String,Int>>.self, capacity: 1) {
$0.pointee
}
print("內(nèi)嵌函數(shù)地址:\(ctx.ptr)")
print("amount值:\(ctx.captureValue.pointee.value)")
print("runningTotal值:\(ctx.captureValue.pointee.valueBox.pointee.obj1.value)")
print("val值:\(ctx.captureValue.pointee.valueBox.pointee.obj2.value)")
print("str值:\(ctx.captureValue.pointee.valueBox.pointee.obj3.value)")
//輸出以下內(nèi)容:
//內(nèi)嵌函數(shù)地址:0x00000001000033f0
//amount值:10
//runningTotal值:3
//val值:5.5
//str值:Zang
- 捕獲值的原理:在堆上開辟內(nèi)存空間,將捕獲的值放到這個內(nèi)存空間里
- 修改捕獲值的時候,去堆上把變量拿出來,修改的就是堆空間里的值
- 閉包是一個引用類型(地址傳遞),底層結(jié)構(gòu)是結(jié)構(gòu)體,包含函數(shù)地址和捕獲變量的值
函數(shù)也是引用類型
func makeIncrementer(inc: Int) -> Int {
var runningTotal = 10
return runningTotal + inc
}
var makeInc = makeIncrementer
將上述代碼生成IR文件:
swiftc -emit-ir main.swift | xcrun swift-demangle
IR
struct FuntionData{
var ptr: UnsafeRawPointer
var captureValue: UnsafeRawPointer?
}
struct VoidIntFun {
var f: (Int) -> Int
}
func makeIncrementer(inc: Int) -> Int {
var runningTotal = 10
return runningTotal + inc
}
var makeInc = VoidIntFun(f: makeIncrementer)
let ptr = UnsafeMutablePointer<VoidIntFun>.allocate(capacity: 1)
ptr.initialize(to: makeInc)
let ctx = ptr.withMemoryRebound(to: FuntionData.self, capacity: 1){$0.pointee}
print("函數(shù)地址:\(ctx.ptr)")
print("捕獲值:\(ctx.captureValue)")
//輸出以下內(nèi)容:
//函數(shù)地址:0x0000000100002070
//捕獲值:nil
搜索符號:
nm
【Mach-O
路徑】| grep
【地址】。在終端搜索地址0000000100002070
,記住沒有前面的0x
nm /Users/x/Library/Developer/Xcode/DerivedData/LGSwiftTest-aqvqasiqlxeaiodejtwafdmetstw/Build/Products/Debug/LGSwiftTest | grep 0000000100002070
輸出
makeIncrementer
內(nèi)嵌函數(shù)的符號0000000100002070 T _$s11LGSwiftTest15makeIncrementer3incS2i_tF
還原符號名稱:
xcrun swift-demangle
【符號】,在終端還原符號名稱,記住沒有前面的_$
xcrun swift-demangle s11LGSwiftTest15makeIncrementer3incS2i_tF
輸出
makeIncrementer
內(nèi)嵌函數(shù)$s11LGSwiftTest15makeIncrementer3incS2i_tF ---> LGSwiftTest.makeIncrementer(inc: Swift.Int) -> Swift.Int
函數(shù)的本質(zhì):函數(shù)是引用類型,底層結(jié)構(gòu)是結(jié)構(gòu)體
{函數(shù)地址,null}
。結(jié)構(gòu)體只有函數(shù)地址,捕獲值為nil
逃逸閉包
逃逸閉包的定義:當(dāng)閉包作為?個實際參數(shù)傳遞給?個函數(shù)時,并且在函數(shù)返回后調(diào)?,我們就說這個閉包逃逸了。當(dāng)我們聲明?個接收閉包作為形式參數(shù)的函數(shù)時,可以在形式參數(shù)前寫
@escaping
來明確閉包是允許逃逸的
案例1:
將閉包賦值給變量
complitionHandler
,存儲后進行調(diào)用,如果?@escaping
修飾閉包后,在逃逸閉包中應(yīng)該顯示引用self
class LGTeacher {
var complitionHandler: ((Int)->Void)?
func makeIncrementer(amount: Int, handler: @escaping (Int) -> Void){
var runningTotal = 0
runningTotal += amount
self.complitionHandler = handler
}
func doSomething(){
self.makeIncrementer(amount: 10) {
print($0)
}
}
deinit {
print("LGTeaher deinit")
}
}
var t = LGTeacher()
t.doSomething()
t.complitionHandler?(10)
//輸出以下內(nèi)容:
//10
complitionHandler
在LGTeacher
的makeIncrementer
?法調(diào)?完成之后才會調(diào)?,此時閉包的?命周期?當(dāng)前?法?命周期更?,需要將閉包聲明成逃逸閉包。此案例沒有打印LGTeaher deinit
,說明內(nèi)部存在循環(huán)引用
案例2:
使用
DispatchQueue.global().asyncAfter
延遲調(diào)用
class LGTeacher {
func makeIncrementer(amount: Int, handler: @escaping (Int) -> Void){
var runningTotal = 0
runningTotal += amount
DispatchQueue.global().asyncAfter(wallDeadline: .now() + 0.1) {
handler(runningTotal)
}
}
func doSomething(){
self.makeIncrementer(amount: 10) {
print($0)
}
}
deinit {
print("LGTeaher deinit")
}
}
var t = LGTeacher()
t.doSomething()
//輸出以下內(nèi)容:
//LGTeaher deinit
//10
makeIncrementer
?法的執(zhí)?,不會等待閉包執(zhí)?完再執(zhí)?,?是直接返回。如果閉包的?命周期??法更?,需要將閉包聲明成逃逸閉包
上述兩個案例,如果不使用
@escaping
修飾,編譯報錯
編譯報錯
- 逃逸閉包的使用,一般都是案例中存儲、延遲這兩種情況。逃逸閉包非常消耗資源,且逃逸閉包可能造成循環(huán)引用,需慎用
- 逃逸閉包中應(yīng)該顯示引用
self
,表示這里有可能產(chǎn)生循環(huán)引用,起到提示作用,沒什么其他目的
非逃逸閉包
Swift 3.0
之后,系統(tǒng)默認閉包為非逃逸閉包,參數(shù)被@nonescaping
修飾,可以通過SIL代碼查看func test(by: () -> Void) { by() }
將上述代碼生成SIL文件:
swiftc -emit-sil main.swift | xcrun swift-demangle
@nonescaping
- 生命周期與函數(shù)一致,閉包只能在函數(shù)體內(nèi)執(zhí)行
- 函數(shù)執(zhí)行完后,閉包表達式消失
- 非逃逸閉包不會產(chǎn)生循環(huán)引用
- 非逃逸閉包編譯器會做優(yōu)化,省略掉一些內(nèi)存管理調(diào)用
- 非逃逸閉包上下文保存棧上,而不是堆上(官方文檔看到的,沒有驗證出來)
自動閉包
func debugOutPrint(_ condition: Bool , _ message: String){
if condition {
print("lg_debug:\(message)")
}
}
debugOutPrint(true, "Application Error Occured")
//輸出以下內(nèi)容:
//lg_debug:Application Error Occured
上述代碼,只有在
conditon
為true
的時候,才會打印傳入的錯誤信息,也就意味著false
的時候當(dāng)前條件不會執(zhí)?
如果當(dāng)前的字符串,需要在某個業(yè)務(wù)邏輯功能中獲取,一般會這樣寫:
func debugOutPrint(_ condition: Bool , _ message: String){
if condition {
print("lg_debug:\(message)")
}
}
func doSomething() -> String{
//do something and get error message
return "NetWork Error Occured"
}
debugOutPrint(true, doSomething())
//輸出以下內(nèi)容:
//lg_debug:NetWork Error Occured
這種寫法會有?個問題,那就是當(dāng)前的
conditon
,?論是true
還是false
,doSomething
?法都會被執(zhí)?。如果doSomething
?法內(nèi)部是耗時的任務(wù)操作,那么這?就造成了資源浪費
這時會想到把當(dāng)前參數(shù)修改成?個閉包,以此來解決資源浪費的問題
func debugOutPrint(_ condition: Bool , _ message: () -> String){
if condition {
print("lg_debug:\(message())")
}
}
func doSomething() -> String{
//do something and get error message
return "NetWork Error Occured"
}
debugOutPrint(true, doSomething)
//輸出以下內(nèi)容:
//lg_debug:NetWork Error Occured
這樣的修改,雖然能滿?僅在符合條件的情況下才執(zhí)行
doSomething
?法,但新問題?隨之?來了。這?是?個閉包,如果輸出的信息是一個需要從外界傳?的String
,那又該怎么辦呢?直接給方法傳入String
是會編譯報錯的
編譯報錯
這種情況可以使?
@autoclosure
關(guān)鍵字,將當(dāng)前的表達式聲明成?個?動閉包。?動閉包不接收任何參數(shù),返回值是當(dāng)前內(nèi)部表達式的值。所以實際上我們傳?的String
就是放在?個閉包表達式中,在調(diào)?的時候返回
func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
if condition {
print("lg_debug:\(message())")
}
}
debugOutPrint(true, "Application Error Occured")
//輸出以下內(nèi)容:
//lg_debug:Application Error Occured
使用
@autoclosure
聲明?動閉包,相當(dāng)于將傳?的String
使用閉包進行包裹,然后又在閉包中將String
直接進行return
。就像下面的代碼一樣:{ return "Application Error Occured" }
- ?動閉包不接收任何參數(shù)
- 返回值是內(nèi)部表達式的值,在調(diào)?的時候返回
使用
@autoclosure
聲明?動閉包,也可以直接傳入函數(shù),且該函數(shù)在未滿足條件時,不會被執(zhí)行
func debugOutPrint(_ condition: Bool , _ message: @autoclosure () -> String){
print("condition當(dāng)前為:\(condition)")
if condition {
print("lg_debug:\(message())")
}
print("----------\n")
}
func doSomething() -> String{
print("執(zhí)行了doSomething函數(shù)")
return "NetWork Error Occured"
}
debugOutPrint(true, doSomething())
debugOutPrint(false, doSomething())
//輸出以下內(nèi)容:
//condition當(dāng)前為:true
//執(zhí)行了doSomething函數(shù)
//lg_debug:NetWork Error Occured
//----------
//
//condition當(dāng)前為:false
//----------