//
// 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