swift4.1 系統(tǒng)學(xué)習(xí)十一 閉包

//
// main.swift
// swift11(閉包)
//
// Created by iOS on 2018/10/10.
// Copyright ? 2018年 weiman. All rights reserved.
//

import Foundation

/*
閉包
閉包是swift中比較重要的一部分內(nèi)容,也是個人認(rèn)為比較難以理解的內(nèi)容照瘾,也可能是之前沒有
細(xì)細(xì)的研究的緣故对粪。

1.閉包的概念

閉包是將一個函數(shù)與執(zhí)行環(huán)境存放在一起的一條記錄叭首。

名字綁定:當(dāng)閉包被創(chuàng)建的時候,它會將它所在的函數(shù)中的局部對象與它自己的執(zhí)行環(huán)境中的對象名做一個值或者
引用的關(guān)聯(lián)映射。這就是“名字綁定”清寇。
捕獲:如果閉包訪問了它所在函數(shù)的局部對象露戒,那么我們稱它“捕獲”了該局部對象。

注意:
由于閉包有自己的執(zhí)行環(huán)境白嘁,我們稱為上下文蔫慧,因此即便當(dāng)它所在的函數(shù)被返回值后,該閉包依然能正常調(diào)用权薯。
問題:那么閉包什么時候被銷毀呢姑躲?

我們之前提到過,捕獲外部函數(shù)局部對象的嵌套函數(shù)屬于命名閉包盟蚣,它也是個閉包黍析。但是,函數(shù)屎开、閉包和方法還是各自獨(dú)立的更好些阐枣。
*/

do {

func foo() {
    print("這是一個函數(shù)")
}

g_func = foo
CTest()

//打印結(jié)果: 這是一個函數(shù)

func boo() {
    var a = 10
    
    func inner() {
        print("這是一個內(nèi)部函數(shù)")
    }
    
    g_func = inner
    CTest()
    
    func closure() {
        a += 10
        print("closure  a = \(a)")
    }
    // 報錯:A C function pointer cannot be formed from a local function that captures context
    // 函數(shù)指針不能指向閉包
    //g_func = closure
    
    g_func = {
        print("哈哈哈哈哈")
    }
    CTest()
    
    struct MyStruct {
        let a: Int, b: Double
        
        func method() {
            print("a = \(a), b = \(b)")
        }
        
        static func typeMethod() {
            print("這是一個結(jié)構(gòu)體")
        }
    }
    
    // 報錯:A C function pointer can only be formed from a reference to a 'func' or a literal closure
   // g_func = MyStruct.typeMethod
    
    
    let obj = MyStruct(a: 10, b: 100.0)
    // 報錯:A C function pointer can only be formed from a reference to a 'func' or a literal closure
   // g_func = obj.method
    
}

boo()

/*
小結(jié):
一個C函數(shù)指針對象只能指向一般的函數(shù),包括不捕獲其外部函數(shù)局部對象的嵌套函數(shù)奄抽,以及不捕獲其外部函數(shù)局部對象的閉包蔼两。

 */

}

// 2. 閉包的定義與調(diào)用
/*
在swift中,一個閉包又稱為“閉包表達(dá)式”逞度。
基本語法形式:
_ = { (參數(shù)1: 參數(shù)類型额划, 參數(shù)2: 參數(shù)類型....) -> 返回值類型 in
閉包執(zhí)行代碼
}

*/
do {

_ = { () -> Void in
   print("這是一個簡單的無參數(shù)的無返回值的閉包")
}

_ = { (a: Int) -> Void in
    print("這是一個有參數(shù),無返回值的閉包档泽,參數(shù):a = \(a)")
}

// 有參數(shù)俊戳,有返回值的閉包揖赴,返回值是 () -> Void
_ = { (a: Int, b: Int) -> () -> Void in
    let sum = a + b
    var x = sum * 2
    
    return { () -> Void in
        print("sum = \(sum)")
        x += sum
    }
}

}

// 閉包的調(diào)用
// 閉包的調(diào)用與函數(shù)的調(diào)用一樣,可以直接在閉包表達(dá)式的后面使用函數(shù)調(diào)用操作符抑胎,形成完整的函數(shù)調(diào)用表達(dá)式燥滑。

do {

print("\n ----------- 閉包的調(diào)用-------------")

_ = { () -> Void in
    print("無參數(shù)無返回值的閉包")
} ()

_ = { (a: Int) -> Void in
    print("a = \(a)")
} (10)

}

// 3. 閉包表達(dá)式的簡略表達(dá)(重點(diǎn)喲)
/*
因?yàn)閟wift語言強(qiáng)大的類型推導(dǎo)特性,我們可以將閉包表達(dá)式簡化阿逃。
*/

do {

//1.缺省返回類型的表達(dá)式
/*
 如果一個函數(shù)的返回類型省略铭拧,則函數(shù)的返回類型就是Void,但是恃锉,如果閉包的返回類型省略羽历,
 則不一定是Void。
 */

let ref = { (a: Int, b: Int) in
    
    return a + b
}

print("value = \(ref(1, 2))")
// 結(jié)果: value = 3

// 上述閉包表達(dá)式中淡喜,返回值就是 (Int, Int)-> Int 類型

//2.缺省參數(shù)類型的閉包表達(dá)式
// swift不僅能夠推導(dǎo)出閉包的返回值類型秕磷,還能推導(dǎo)出參數(shù)的類型,因此炼团,閉包的形參也是可以省略的澎嚣。
// 并且小括號也是可以省略的。如果閉包形參列表的圓括號省略了瘟芝,那么每個形參的類型都必須缺省易桃。

var ref2: (Int, Int) -> Int = { a, b in
    return a + b
}
print("1: \(ref2(2, 3))")

ref2 = { a, b -> Int in
    return a + b
}
print("2: \(ref2(4, 6))")

ref2 = { (a, b: Int) in
   return a + b
}
print("3: \(ref2(4, 5))")

// 3. 缺省return關(guān)鍵字
// 如果閉包表達(dá)式中其執(zhí)行語句只有一個單獨(dú)的 return語句構(gòu)成,那么關(guān)鍵字return也可以缺省

let ref3: (Int, Int) -> Int = { a, b in
    a + b
}
print("value = \(ref3(1, 1))")

// 返回值是一個元組的第二個元素
print("\n")
let ref4 = { (a: Int) -> Int in
    (print("a = \(a)"), a + 1).1
}
print("value2 = \(ref4(3))")

// 4. 省略 in 關(guān)鍵字
// 如果一個閉包確定其每個形參的類型以及返回類型锌俱,那么該表達(dá)式中連 in 關(guān)鍵字都能缺省晤郑。
// 當(dāng)我們要引用其形參時使用 $ 符號加上形參索引:第一個形參的索引值為0,第二個索引值為1,后續(xù)參數(shù)
// 依次類推贸宏。
print("\n")
var r: (Int, Int) -> Int  = { $0 + $1 }
print("value: \(r(3, 3))")

let r2 = {
    print("hello")
}
r2()

r = {
    print("第一個參數(shù): \($0)")
    print("第二個參數(shù): \($1)")
    return $0 + $1
}
print("result: \(r(5, 8))")


let r3: (Int) -> Void  = {
    print("參數(shù): \($0)")
    let a = $0 * $0
    print("a = \(a)")
}
r3(5)

}

// 4. 尾隨閉包
/*
如果一個閉包表達(dá)式作為函數(shù)調(diào)用的最后一個實(shí)參造寝,那么我們可以采用“尾隨閉包”語法糖。
*/

do {

print("\n ----------尾隨閉包---------------")

func foo(_ a: Int, callback: (Int) -> Void) {
    callback(a)
}

foo(10) { (aa) in
    print("參數(shù)是 \(aa)")
}
//或者簡寫
foo(12, callback: { print("參數(shù): \($0)")})

//尾隨閉包, 這個時候callback標(biāo)簽必須省略
foo(14){
    print("參數(shù)是: \($0)")
}

func boo (_ a: Int, _ b: Int, callback: ((Int, Int) -> Int)?) {
    if let callback = callback {
        let value = callback(a, b)
        print("value = \(value)")
    }
}

boo(2, 3) { (a: Int, b: Int) -> Int in
    return a * a + b * b
}

}

// 5. 捕獲局部對象與閉包執(zhí)行上下文
// 閉包所捕獲的對象是按照引用進(jìn)行捕獲的吭练,同時閉包本身是一個引用對象诫龙。

do {
print("\n ----------捕獲局部對象與閉包執(zhí)行上下文---------------")

var a = [1, 2, 3]
var b = a
a[0] += 10

print("a = \(a)")
// 結(jié)果: a = [11, 2, 3]

func foo() {
    var a = 10
    let closure = {
        print("a = \(a)")
    }
    
    closure()
    
    a += 10
    
    closure()
}

foo()

/*
 結(jié)果:
 a = 10
 a = 20
 */

func boo() -> () -> Void {
    var x = 10
    return {
        x += 10
        print("x = \(x)")
    }
}

var closure = boo()

var ref = closure

closure()

ref()

closure()

/*
 
 結(jié)果:
 x = 20
 x = 30
 x = 40
 */

// 重新賦值后再次調(diào)用
ref = boo()

closure()

ref()

/*
 結(jié)果:
 x = 50
 x = 20
 */

/*
 小結(jié):
 上述實(shí)例證明了閉包屬于引用類型,閉包所捕獲的外部局部對象也是以引用的方式 捕獲的鲫咽。
 */

}

// 6. 逃逸閉包
/*
如果我們定義了一個函數(shù)签赃,它有一個形參為函數(shù)類型,如果在此函數(shù)中將通過異步的方式調(diào)用該函數(shù)引用對象分尸,
那么我們需要將此函數(shù)類型聲明為“逃逸的”锦聊,以表示該函數(shù)類型的形參對象將可能在函數(shù)調(diào)用操作結(jié)束后的某一
時刻才會被調(diào)用。
*/

do {

print("\n --------逃逸閉包----------")

var ref: (() -> Void)!

func foo(closure: @escaping () -> Void) {
    ref = closure
}

foo {
    print("你好")
}

ref()

}

// 7. 自動閉包
/*
自動閉包也是swift中的一個語法糖箩绍。如果一個函數(shù)的某個形參為函數(shù)類型孔庭,并且不含有任何形參,那么我們
就可以將此參數(shù)聲明為“自動閉包”伶选。
*/

do {

print("\n---------自動閉包----------")

func foo(closure: () -> Void) {
    closure()
}

foo {
    print("簡單的閉包")
}

func boo(auto closure: @autoclosure () -> Void) {
    closure()
}

boo(auto: print("這是個自動閉包"))

func doo() {
    let a = 1, b = -1
    
    func isLarger(compare: @autoclosure () -> Bool) -> Bool {
        return compare()
    }
    
    let result = isLarger(compare: a > b)
    print("result: \(result)")
}

doo()

/*
 小結(jié):
 不推薦各位在制作公共的API的時候使用自動閉包史飞。
 */

}

輔助文件:

//
//  Test.h
//  swift11(閉包)
//
//  Created by iOS on 2018/10/10.
//  Copyright ? 2018年 weiman. All rights reserved.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Test : NSObject

extern void (^ _Nullable g_block)(void);
// 指向函數(shù)的指針
extern void (* _Nullable g_func)(void);

extern void CTest(void);


@end

NS_ASSUME_NONNULL_END

//
//  Test.m
//  swift11(閉包)
//
//  Created by iOS on 2018/10/10.
//  Copyright ? 2018年 weiman. All rights reserved.
//

#import "Test.h"

@implementation Test

void (^ g_block)(void) = NULL;

void (* g_func)(void) = NULL;

void CTest(void) {
    
    if (g_block != NULL) {
        g_block();
    }
    
    if (g_func != NULL) {
        g_func();
    }
}

@end

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末尖昏,一起剝皮案震驚了整個濱河市仰税,隨后出現(xiàn)的幾起案子构资,更是在濱河造成了極大的恐慌,老刑警劉巖陨簇,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吐绵,死亡現(xiàn)場離奇詭異,居然都是意外死亡河绽,警方通過查閱死者的電腦和手機(jī)己单,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耙饰,“玉大人纹笼,你說我怎么就攤上這事」豆颍” “怎么了廷痘?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長件已。 經(jīng)常有香客問我笋额,道長,這世上最難降的妖魔是什么篷扩? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任兄猩,我火速辦了婚禮,結(jié)果婚禮上鉴未,老公的妹妹穿的比我還像新娘枢冤。我一直安慰自己,他們只是感情好铜秆,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布掏导。 她就那樣靜靜地躺著,像睡著了一般羽峰。 火紅的嫁衣襯著肌膚如雪趟咆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天梅屉,我揣著相機(jī)與錄音值纱,去河邊找鬼。 笑死坯汤,一個胖子當(dāng)著我的面吹牛虐唠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播惰聂,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼疆偿,長吁一口氣:“原來是場噩夢啊……” “哼咱筛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起杆故,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤迅箩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后处铛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饲趋,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年撤蟆,在試婚紗的時候發(fā)現(xiàn)自己被綠了奕塑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡家肯,死狀恐怖龄砰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讨衣,我是刑警寧澤换棚,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站值依,受9級特大地震影響圃泡,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜愿险,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一颇蜡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辆亏,春花似錦风秤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至彻磁,卻和暖如春碍沐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衷蜓。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工累提, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人磁浇。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓斋陪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子无虚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學(xué)習(xí)記錄文檔缔赠,今天18年5月份再次想寫文章,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,739評論 2 9
  • 1友题、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,969評論 3 119
  • 1.VFL(Visual Format Language) 先定義一個字典嗤堰,將要布局的對象和他的字符串對應(yīng)起來,因...
    終極解碼者閱讀 234評論 0 0
  • 有些風(fēng)景 親身領(lǐng)略才能定格在記憶的相冊 認(rèn)真走過的路 才會留下印記隨著時光藏進(jìn)心里 那段最美的風(fēng)景咆爽、最美的時光 是...
    云路閱讀 146評論 0 0
  • (一) 我是一只黑貓梁棠,我已經(jīng)在人世間流浪了十多年置森。 其實(shí)斗埂,作為貓的生命,我早已經(jīng)死了凫海,死在了九年前那場八級大地震中...
    典典的蟹媽閱讀 1,132評論 24 30