闭包的定义
// 定义语法
//{
//   (参数列表) ->返回值类型 in
 //     语句组
 //}
        
// 声明一个闭包(有两个整形参数,且返回值为整形的闭包)
var sumClosure: ((_ a: Int, _ b: Int) -> Int)
        
// 实现闭包
sumClosure = {(a: Int, b: Int) -> Int in
   return a + b
}
        
// 调用
let sum = sumClosure(10,20)
print(sum)
Swift闭包 简化写法
// 形式1: 带有参数参数类型,参数名,返回值类型
 sumClosure = { (a: Int, b: Int) -> Int in return a + b}
// 形式2: 省略参数类型
sumClosure = { (a,b) -> Int in return a + b}
// 形式3: 省略返回值类型
sumClosure = { (a,b) in return a + b}
// 形式4:省略参数小括号
sumClosure = { a,b in return a + b}
// 形式5: 省略参数
sumClosure = { return $0 + $1}
// 形式6: 省略关键字
returnsumClosure = { $0 + $1}
Swift 闭包常用方式
作为非空变量:
var closureName: (ParameterTypes) -> ReturnType
作为可为空变量:
var closureName: ((ParameterTypes) -> ReturnType)?
作为别名:
typealias ClosureType = (ParameterTypes) -> ReturnType
作为静态变量:
let closureName: ClosureType = { ... }
作为参数(用别名定义),在函数中:
funcName(parameter: (ParameterTypes) -> ReturnType)
注释: 如果在闭包中的参数会函数体外变化,需要添加修饰词@escaping.
作为函数回调的参数:
funcName({ (ParameterTypes) -> ReturnType in statements })
作为函数的参数(不用别名定义):
array.sorted(by: { (item1: Int, item2: Int) -> Bool in return item1 < item2 })
作为函数的参数(不用别名定义),并隐藏参数类型(比如这里的item1: Int):
array.sorted(by: { (item1, item2) -> Bool in return item1 < item2 })
作为函数的参数(不用别名定义),并隐藏返回值类型(比如返回值类型为Bool):
array.sorted(by: { (item1, item2) in return item1 < item2 })
作为函数的最后一个参数:
array.sorted { (item1, item2) in return item1 < item2 }
作为函数的最后一个参数, 省略掉参数声明:
array.sorted { return $0 < $1 }
作为函数的最后一个参数, 省略掉返回return关键字:
array.sorted { $0 < $1 }
作为函数的最后一个参数, 省略掉返回具体实现,只用符号表示比较:
array.sorted(by: <)
作为函数的参数,并清楚写清所有参数,返回值,类型,以及实现:
array.sorted(by: { [unowned self] (item1: Int, item2: Int) -> Bool in return item1 < item2 })
作为函数的参数,并清楚写清所有参数,返回值,以及实现。其中省略掉参数类型,返回值类型,由上下文推论得出:
array.sorted(by: { [unowned self] in return $0 < $1 })
闭包、嵌套函数、函数
更好地使用闭包前需要理清3者的联系和区别
首先看3种函数的定义:
//函数
func eatTomatos(a: Int, b: Int) -> Int {
    return a + b
}
//嵌套函数
func eatTomatos(a: Int, b: Int) -> Int {
    //嵌套函数
    func digest(a: Int, b: Int) -> Int {
        return 2 * a + b
    }
    
    return digest(a: a, b: b)
}
//闭包
var eatTomatos = {(a: Int, b: Int) -> Int in
    return a + b
}
从上面的定义可以看出函数和嵌套函数其实是一回事,唯一的区别是,嵌套函数是定义在一个函数内部的函数,对外部是隐藏的,只能在其定义的函数内部有效。而闭包与函数的不同要多一些:1、不需要使用func关键字,2、其次函数有名称如:eatTomatos,而闭包是没有名称的,3、闭包的参数和函数体都要使用{ }包起来,在参数后要使用in关键字连接函数体,4、闭包可以作为一种类型赋值给一个变量,上面代码中的闭包类型是:(Int, Int) -> Int。
上面从定义上分析了3者的不同,下面从功能上区分下。
1、函数是全局的,不能捕获上下文中的变量;而嵌套函数和闭包可以直接嵌套在上下文中使用的,因此可以捕获上下文中的变量,需要注意的是每一个闭包都只会持有一个它所捕获的变量的副本,如下:
override func viewDidLoad() {
        super.viewDidLoad()
        print(eatTomatos(a: 1, b: 2))//③
        print(eatTomatos(a: 2, b: 3))//④
}
func eatTomatos(a: Int, b: Int) -> Int {
    var numArray: Array<Int> = Array.init()
    
    //嵌套函数
    func digest(a: Int, b: Int) -> Int {
        numArray.append(a)
        numArray.append(b)
        print(numArray.count)//②
        return 2 * a + b
    }
    
    print(numArray.count)//①
    
    return digest(a: a, b: b)
}
//打印的结果依次(①②③④)是:
0
2
4
0
2
7
2、闭包可以作为参数或者返回值,如下:
// 作为参数
override func viewDidLoad() {
    super.viewDidLoad()
    cookTomates { (a, b) in
        print(a)
        print(b)
    }
}
func cookTomates(tomato: (Int, Int) -> Void){
    tomato(1, 2)
}
cookTomates函数将闭包(Int, Int) -> Void作为参数,并且可以在函数内部操作这个闭包 在调用cookTomates函数式需要将给这个闭包参数赋值,并且闭包中的参数名需要调用的时候自行命名。
//作为返回值
override func viewDidLoad() {
    super.viewDidLoad()
    
    let tomato = gainTomatos()
    print(tomato(2, 3))
    
}
var eatTomatos: (Int, Int) -> Int = {(a: Int, b: Int) -> Int in
    return a + b
}
func gainTomatos() -> (Int, Int) -> Int {
    return eatTomatos
}
函数gainTomatos将闭包(Int, Int) -> Int作为返回值,这里返回的是(Int, Int) -> Int的一个实例,调用者便可以利用返回的实例获取(Int, Int) -> Int闭包处理参数的逻辑,实现代码的传递和复用
为你的闭包类型起别名
闭包类型不像其他常用类型看起来比较简洁,有参数、返回值、关键字、符号构成,影响阅读和纠错,因此为常用的闭包类型起一个别名很有必要。
如下,为(Int, Int) -> Int闭包类型起别名
typealias Tomato = (Int, Int) -> Int
因此上面闭包当做返回值使用的代码便可以改写如下:
override func viewDidLoad() {
    super.viewDidLoad()
    
    let tomato = gainTomatos()
    print(tomato(2, 3))
    
}
var eatTomatos: Tomato = {(a: Int, b: Int) -> Int in
    return a + b
}
func gainTomatos() -> Tomato {
    return eatTomatos
}
当我们把(Int, Int) -> Int类型抽象为Tomato后,不仅仅是代码看起来更加简洁,也更接近我们使用的其他参数类型,更加便于理解
闭包传值
OC中常用的传值方法有代理、Block、通知等,对应到Swift Block就由闭包替代。
如下需要使用闭包将B中的a、b值传递到A中
override func viewDidLoad() {
    super.viewDidLoad()
    
    let a: A = A()
    a.fromB()
    
}
typealias Tomato = (Int, Int) -> Int
class A: NSObject {
    let b: B = B()
    
    func fromB() {
        b.tomato = {
            (x, y) -> Int in
            return x + y
        }
        print(b.toA())
    }
    
}
class B: NSObject {
    var tomato: Tomato?
    
    func toA() -> Int {
        let a = 3
        let b = 4
        return tomato!(a, b)
    }
    
}
由上可以总结出闭包传值的流程: 1️⃣首先为自己的闭包类型起一个别名,便于使用; 2️⃣在需要把值传递给另外一个对象的类里声明一个闭包类型的变量,对应到上面的代码中就是B; 3️⃣在需要接收值的类里为闭包类型赋值,从而在此闭包内便可以获取传递的值。
注意: 这里着重描述传值的流程,在开发的时候还需判断闭包是否为nil,否则会导致崩溃;
闭包作为参数传值
在使用AFN或者SDWebImage的时候,通过Block获取请求的数据很方便,那么在Swift中如何使用闭包实现这种效果呢。
其实上面在说闭包作为参数使用的时候,已经实现了这种传值的方式,这里举另外一个例子,我们在使用第三方库的时候通常会将其再封装一次,避免由于第三方库不维护或者出现较大更新的时候增加不必要的工作量,这里以简单封装Alamofire为例,代码如下:
import UIKit
import Alamofire
import SwiftyJSON
class ZYLResponse: NSObject {
    //接收数据是否成功
    var isSuccess: Bool = false
    //接收到的字典数据
    var dict: Dictionary<String, Any>?
    //接收到的数组数据
    var array: Array<Any>?
    //错误信息
    var error: Error?
    //JSON
    var json:JSON?
    
}
typealias DataReply = (ZYLResponse) -> Void
class ZYLNetTool: NSObject {
    
    ///POST请求
    open static func post(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
        Alamofire.request(url, method: .post, parameters: parameters).responseJSON { (response) in
            let myResponse = ZYLResponse()
            myResponse.isSuccess = response.result.isSuccess
            myResponse.dict = response.result.value as! Dictionary<String, Any>?
            myResponse.array = response.result.value as? Array<Any>
            myResponse.error = response.result.error
            myResponse.json = JSON(data: response.data!)
            
            complete(myResponse)
        }
    }
    
    ///GET请求
    open static func get(url: String, parameters: Dictionary<String, Any>?, complete: @escaping DataReply) {
        Alamofire.request(url, method: .get, parameters: parameters).responseJSON { (response) in
            let myResponse = ZYLResponse()
            myResponse.isSuccess = response.result.isSuccess
            myResponse.dict = response.result.value as! Dictionary<String, Any>?
            myResponse.array = response.result.value as? Array<Any>
            myResponse.error = response.result.error
            myResponse.json = JSON(data: response.data!)
            
            complete(myResponse)
        }
    }
    
}
//调用
        ZYLNetTool.post(url: HTTP_ACTIVATE_PORT, parameters: paraDict, complete: { (response) in
            if response.isSuccess {
                //请求数据成功
               
            } else {
                //请求数据失败
            }
        })
注意: 1、使用闭包时要注意管理内存; 2、当作闭包为函数参数使用时可以脱离函数独立使用时,要将此闭包声明为逃逸闭包,在参数类型前面加上@escaping,否则会报错。
尾巴
接触一种新的事物之前总会觉得很难,当我们学会后发现其实很简单,难的不是这个新事物本身,而是我们的大脑出于习惯很难接受新的事物,总是需要一定的过程。记得学C语言时很难理解指针,学C++时很难理解面向对象,学OC时很难理解Block,而Swift作为一种新的语言,必然会有很多新的事物让我们难以理解,比如闭包、元组、可选类型、函数式编程等等,本文只对闭包发表一点拙见,还望指正,谢谢。









网友评论