函數(shù)
是一種特殊的閉包
// 無參無返回值的三種寫法
func a() -> Void { ... }
func a() -> () { ... }
func a() { ... } //最簡
// 有參數(shù)無返回值
func square(a: Int, b: Int) -> Int { return a * b }
// 有參數(shù)有返回值
func square(a: Int, b: Int) { ... } //最簡
內(nèi)部函數(shù)
在函數(shù)體內(nèi)部定義的函數(shù)暇务,調(diào)用需在聲明后
func result() {
func sum(a: Int, b: Int) -> Int {
return a + b
}
//在內(nèi)部函數(shù)聲明的下面調(diào)用
print(sum(a: 20, b: 80))
}
外部參數(shù)及忽略
提供外部參數(shù)能增強(qiáng)函數(shù)的可讀性刺桃,同時(shí)也能夠讓函數(shù)在內(nèi)部使用參數(shù)更加簡單
忽略外部參數(shù)用 “_” 表示
// frist/second表示外部參數(shù)兆蕉,a標(biāo)識函數(shù)的內(nèi)部參數(shù)只能夠在函數(shù)的內(nèi)部使用
func sum(first a: Int, second b: Int) -> Int {
return a + b
}
sum(first: 10, second: 20);
// 忽略外部參數(shù) “_”
func sum(_ a: Int, _ b: Int) -> Int {
return a + b
}
sum(10, 20);
函數(shù)小技巧
多返回值:元組類型
oc 中使用輸出參數(shù)實(shí)現(xiàn)
使用元組來讓一個(gè)函數(shù)返回多個(gè)值。該元組的元素可以用名稱或數(shù)字來表示。
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
//let statistics: (min: Int, max: Int, sum: Int)
let statistics = calculateStatistics(scores:[5, 3, 100, 3, 9])
//或者print(statistics.2) 該元組的元素可以用名稱或數(shù)字來表示
print(statistics)
不確定參數(shù)
oc 中使用 va_list 等宏實(shí)現(xiàn)
參數(shù)個(gè)數(shù)可變:函數(shù)可以帶有可變個(gè)數(shù)的參數(shù)答憔,這些參數(shù)在函數(shù)內(nèi)表現(xiàn)為數(shù)組的形式
func nameFunc(names: String...) {
for name in names {
print(name, terminator: " ")
}
}
nameFunc(names: "zhangsan", "lisi", "wangwu")
// Prints "zhangsan lisi wangwu "
閉包
closure:Swift 中的 block
閉包是一個(gè)封閉的結(jié)構(gòu): 閉包的參數(shù)喳挑,返回值彬伦、需要執(zhí)行的代碼片段 都應(yīng)該在閉包的內(nèi)部
函數(shù)和閉包都是引用類型:無論你將函數(shù)或閉包賦值給一個(gè)常量還是變量,你實(shí)際上都是將常量或變量的值設(shè)置為對應(yīng)函數(shù)或閉包的引用
// block
void(^callback)(NSString *) = ^(NSString *str) {
// code
}
// closure
let closure = { (str: String) -> () in
// code
}
寫法
一般左邊不寫伊诵,參數(shù)類型和返回值類型在閉包內(nèi)標(biāo)明即可
- 無參無返回值
// 完整形式 左右都有 標(biāo)明閉包類型
let closure:() -> () = { () -> () in
print("最簡單的閉包完整形式")
}
// 簡化:左邊不寫单绑,右邊標(biāo)明閉包類型,返回值為空時(shí)曹宴,寫()和 Void 一樣
let closure = { () -> Void in
print("返回值為空時(shí)")
}
// 最簡形式
let closure = {
print("最簡形式")
}
- 有參無返回值
let closure = { (a: Int, b: Int) in
print("\(a) + \(b) = \(a+b)")
}
- 有參有返回值
let closure = { (a: Int, b: Int) -> (String) in
return "\(a+b)"
}
尾隨閉包
當(dāng)函數(shù)的最后一個(gè)參數(shù)是閉包的時(shí)候搂橙,函數(shù)的參數(shù)的 '()' 可提前關(guān)閉。
如果函數(shù)只有閉包這一個(gè)參數(shù), '()' 可以省略笛坦,如果閉包類型是無參無返回值区转,"() -> () in" 也可以省略苔巨。
是在調(diào)用函數(shù)的時(shí)候可以省略,而不是聲明的時(shí)候
bug:如果寫出來的閉包函數(shù)報(bào)錯(cuò)废离,可以在聲明的時(shí)候把返回值寫上侄泽,調(diào)用成功后再刪除
func loadData(name: String, completed: (Int) -> ()) {
completed((name as NSString).length)
}
loadData(name: "abc") { (count) in
print("name's count = " + "\(count)")
}
//Prints "name's count = 3"
func cFunc(closure: () -> ()) {
closure()
}
cFunc {
// code
}
@escaping
逃逸閉包:當(dāng)一個(gè)閉包作為參數(shù)傳到一個(gè)函數(shù)中,但是這個(gè)閉包在函數(shù)返回之后才被執(zhí)行蜻韭,我們稱該閉包從函數(shù)中逃逸悼尾。當(dāng)你定義接受閉包作為參數(shù)的函數(shù)時(shí),你可以在參數(shù)名之前標(biāo)注 @escaping肖方,?來指明這個(gè)閉包是允許“逃逸”出這個(gè)函數(shù)的
@escaping:標(biāo)識該閉包可以逃離當(dāng)前的“語境”闺魏,寫在閉包類型聲明之前,閉包作為函數(shù)的參數(shù)是默認(rèn)不可逃逸的窥妇。
@escaping 語境:
-
閉包在 async 需要逃逸舷胜、sync 不需要逃逸
-
閉包被引用,需要逃逸活翩。
循環(huán)引用
方法一:[weak self] (推薦)
self 是可選項(xiàng)烹骨,如果 self 已經(jīng)被釋放,則為 nil
// 類似 OC 中:__weak typeof(self) weakSelf;
// 如果 self 已經(jīng)被釋放材泄,則為 nil
loadData { [weak self] in
print("\(self?.view)")
}
方法二: [unowned self] (不推薦)
self 不是可選項(xiàng)沮焕,如果 self 已經(jīng)被釋放,則出現(xiàn)野指針訪問
// 類似 OC 中:__unsafe_unretained typeof(self) weakSelf;
// 如果 self 已經(jīng)被釋放拉宗,則出現(xiàn)野指針訪問
loadData { [unowned self] in
print("\(self.view)")
}
方法三:與 OC 類似
weak var weakSelf = self
loadData() {
print("\(weakSelf?.view)")
}
網(wǎng)絡(luò)請求試寫
func request(urlStr: String, compeletd:@escaping (Bool, Any) -> Void) {
guard let url = URL(string: urlStr) else {
return
}
DispatchQueue.global().async {
URLSession.shared.dataTask(with: url) { (data, _, error) in
if error != nil {
DispatchQueue.main.async {
compeletd(false, error!)
}
} else {
let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
DispatchQueue.main.async {
compeletd(true, json!)
}
}
}.resume()
}
}