搜索
您的当前位置:首页正文

Swift中闭包介绍和使用

来源:二三娱乐

闭包是自包含的函数代码块,可以在代码中传递和使用。闭包表达式是一种利用间接语法构建内联闭包的方式。
闭包的表达式(含闭包参数)

{ (参数列表)->返回值类型  in
     语句
}

在这里关键字in表示闭包的参数和返回值类型已经完成,闭包函数体即将开始。

/*无参数形式的闭包表达式语法形式如下:
{() -> 返回值类型 in 
}
定义的语法:
let/var  闭包表达式常量名称/闭包表达式变量名称 = 无参数的表达式
调用方法:
闭包表达式常量名称/闭包表达式变量名称()*/
let printStr = { () ->Void in
    print("Swift的闭包")
    
}
printStr()
/*
有参数形式的闭包表达式语法形式如下:
{(参数列表) -> 返回值类型 in 
    语法块
}
定义的语法:
let/var  闭包表达式常量名称/闭包表达式变量名称 = 有参数的表达式
调用方法:
闭包表达式常量名称/闭包表达式变量名称(参数列表)*/
*/
let add = { (x:Int,y:Int) ->Int in
    return x+y
}

print(add(10,20))

上面主要对闭包的定义进行的介绍,其实闭包表达式最常用于其他函数中,并不是单独去使用。
1、把闭包表达式作为函数的参数

//定义函数,将闭包表达式放到函数的参数里面
func copare (arr:[Int],value:Int,cb:(_ Num:Int,_ Value:Int)->Bool)->Bool{
    for item in arr{
        if(cb(item,value)){
            return true;
        }
    }
    return false;
}
let array = [20,90,100,50,60]
//调用函数并且实现完成闭包表达式的代码块
let v1 = copare(arr: array, value: 100) { (num:Int, value01:Int) -> Bool in
    return num > value01
};

可以从上面的copare函数中可以看出第三个参数是一个闭包闭包的类型为(Num:Int,Value:Int)->Bool
注意:
(1)在调用函数时可以省略闭包的返回值
(2) 如果标保中只有一行代码,那么可以省略掉return
2、Trailing闭包:如果开发者需要将一个很长的闭包表达式作为最后一个参数传递给函数,可以使用Trailling闭包,他可以增前函数的可读性。


//例子 一
let arrty:Array = [10,90,80,20]
//方法的最后一个参数为(_ Num:Int,_ value:Int)->Int
func trailingClosure01(array arr:[Int],closure:(_ Num:Int,_ value:Int)->Int){
    for item in arr {
        print(closure(10,item))
    }
    
}
//此为不使用traling闭包的函数调用
trailingClosure01(array: arrty, closure: {(num1:Int,num2:Int)->Int in
    return num2+num1;
})
//此为是使用traling闭包的函数调用
trailingClosure01(array: arrty) { (num1, num2) -> Int in
    return num2 - num1
}

//例子二
func trailingClosure02(closure: ()->Void)->Void{
    closure()
}
//此为不使用traling闭包的函数调用
trailingClosure02(closure: {()->Void in
     print("没有使用traling闭包调用函数")
})
//此为是使用traling闭包的函数调用
trailingClosure02() {
    print("使用traling闭包调用函数")
}

3、捕获值:闭包可以自起定义的上下文中捕获常量和变量

/*
 例子1、该函数的返回值是闭包(闭包是没有参数,返回值为Int)
 */
func captureValue(forIncrement amount:Int)->()->Int
{
    var runningTotal = 0;
    /*
     //这是没有使用tralling包的定义包
    return {
        ()->Int in
        runningTotal += amount
        return runningTotal};*/
    //定义函数 captureValueClosure()
    func captureValueClosure()->Int{
        runningTotal += amount
        return runningTotal
    }
    return captureValueClosure;
    //上面两个return可以看出闭包是自包含的函数代码块
}

let value1 = captureValue(forIncrement: 10);
print(value1())
print(value1())
print(value1())
print(value1())
print(value1())

4、下面写一种闭包表达式在UI程序中的使用的例子,如图:

closure.gif

自定义的View的代码如下:

import UIKit
//创建一个闭包类型
typealias ClosureClick = ()->Void;
class TestView: UIView {
    override func willMove(toSuperview newSuperview: UIView?) {
        let btnChangSuperBackgroundColor = UIButton.init(frame: CGRect(x: 10, y: 130, width: (newSuperview?.frame.size.width)! - 20, height: 40))
        btnChangSuperBackgroundColor.backgroundColor = UIColor.blue;
        btnChangSuperBackgroundColor.setTitle("点击修改父窗口的颜色", for: .normal)
        self.addSubview(btnChangSuperBackgroundColor);
        
        btnChangSuperBackgroundColor.addTarget(self, action:#selector(TestView.btnClick), for:.touchUpInside );
    }
    func btnClick() -> Void {
        //在这里调用闭包
        if (myClosure != nil) {
            myClosure!()
        }
    }
    //为自定义的VIew 创建一个闭包的类型的属性
    var myClosure:ClosureClick?

}

UIViewController中实现的代码如下:

override func viewDidLoad() {
        super.viewDidLoad()
        
        let testView  = TestView();
        //用来点击按钮后实现随机颜色的数组
        let colorArray = [UIColor.red,UIColor.black,UIColor.blue]
        testView.frame = CGRect(x: 0, y: 20, width: self.view.bounds.size.width, height: 300)
        testView.backgroundColor = UIColor.brown
        //在这里实现闭包表达式
        testView.myClosure = {
            let index:Int  = Int(arc4random_uniform(3));
            self.view.backgroundColor = colorArray[index];
        }
        
        self.view.addSubview(testView);
    }

5、Swift中闭包的循环引用
循环引用可以简单的理解为A引用了B,而B又引用A双蛋都同时保持对方的一个引用,在释放的时候A和B都会的等待对方先释放,这样两个都不会释放,造成内存的泄露,并且如果过于频繁或者两者的处理量比较大时会发生崩溃。如何判断类的循环引用,需要重新类的析构函数如下:

deinit {
        print("销毁了"+NSStringFromClass(self.classForCoder));
    }

以下为几种常见的对循环引用的解决方案。
方案一:
仿照OC 解决处理循环引用用weak进行修饰,将上面的中的闭包的实现修改,建议在开发总使用该解决方案
方案二:
处理循环引用[weak self]来完成
方案三:
处理循环引用[unowned self]
三种方案的具体代码如下:

import UIKit
//创建一个闭包类型
typealias ClosureClick = ()->Void;
class TestView: UIView {
    //为自定义的VIew 创建一个闭包的类型的属性
    var myClosure:ClosureClick?
    //给类添加一个属性
    var classNum:Int?
    //这里就不用提前调用闭包了
    var testClosure:((Int)->Void)?
    func btnClick() -> Void {
        //在这里调用闭包
        if (myClosure != nil) {
            myClosure!()
        }
    }
    func testC(myNum:Int) -> Void {
        //产生循环引用的
        testClosure = {(myNum)->Void in
            self.classNum = myNum;
        }
        //方案1
        weak var weakSelf = self
        testClosure = {(myNum)->Void in
            weakSelf?.classNum = myNum;
        }
        //方案2
        //[weak self] 表示 self为可选型 可以为nil 所以在使用的时候必须解包
        testClosure = {[weak self](myNum)->Void in
            self?.classNum = myNum;
        }
        //方案3
        //[unowned self]由于在使用前要保证一定有这个对象 所以不必解包
        testClosure = {[unowned self](myNum)->Void in
            self.classNum = myNum;
        }
        
    }
    deinit {
        print("销毁了"+NSStringFromClass(self.classForCoder));
    }

}

类的创建和调用:

 var testView:TestView? = TestView()
        //[unowned self]
        testView?.testC(myNum: 10)
        testView = nil
Top