1. 程式人生 > >Swift學習之十四:閉包(Closures)

Swift學習之十四:閉包(Closures)

/* 閉包(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