美文网首页工作生活
swift 闭包学习记录

swift 闭包学习记录

作者: 宋鸿康iOS | 来源:发表于2019-07-01 17:12 被阅读0次

    在OC开发中,我们经常用block,block原理我估计应该和swift的闭包差不多,现在一起看看swift中的闭包把。

    闭包的表达式

    {
        (parameterTypes) -> (returnType) in
         statements 需要执行的代码
    }
    

    长话短说,看几种闭包的写法,体验下

    let addClosure = {
        (num1: Int,num2: Int) -> Int  in
        return num1 + num2
    }
    
    let result = addClosure(10, 20)
    

    是不是清晰了许多,闭包和block,就是保存一段代码

    闭包表达式简写

    闭包作为函数函数的参数情况,看看下面几个例子,你就明白了

    func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
        let sum = fn(v1,v2)
        print(sum)
    }
    
    1. 最原始的调用写法
    exec(v1: 5, v2: 20, fn: {
        (v1: Int, v2: Int) -> Int in
        return v1 + v2
    })
    
    1. 去掉闭包的返回值写法
    exec(v1: 5, v2: 20, fn: {
        (v1: Int, v2: Int)  in
        return v1 + v2
    })
    
    1. 在第二步基础上再去掉形参
    exec(v1: 5, v2: 10, fn: {
        (v1, v2) in
        return v1 + v2
    })
    
    1. 在第三步基础上去去掉return
    exec(v1: 5, v2: 10, fn: {
        (v1, v2) in v1 + v2
    })
    
    1. 闭包的语法糖调用,用0,1,$2 ...代表闭包的实际参数
    exec(v1: 5, v2: 10, fn: {$0 + $1})
    
    1. 如果你觉得上面的调用够精简了,那你就太容易满足了
    exec(v1: 5, v2: 10, fn: -)
    

    闭包取别名

    取别名的意思很好理解,比如我叫宋鸿康,在公司一般都叫我宋工,在公司叫我宋工,我就知道叫的是我,取别名的好处,具体看看几个例子,你看是否有简化
    回顾下以前用OC给block取别名

    typedef void(^blockName)(void);
    

    在swift中取别名

    typealias Fn = (Int, Int) -> (Int)
    

    上面例子的swift参数用Fn代替,看代码

    typealias Fn = (Int, Int) -> (Int)
    
    func exec(v1: Int, v2: Int, fn: Fn) {
        let sum = fn(v1,v2)
        print(sum)
    }
    exec(v1: 5, v2: 10, fn: -)
    

    这就是取别名的用法,和oc中的block取别名用法一样,没啥好说的

    尾随闭包

    尾随闭包是指闭包表达式作为函数最后一个实际参数传递给函数时,我们叫这个闭包尾随闭包,概念有点苦涩,看看例子

    func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
        let sum = fn(v1,v2)
        print(sum)
    }
    

    fn 闭包作为函数exec 最后一个参数。fn就是尾随闭包,尾随闭包在使用的时候可以精简

    func exec(v1: Int, v2: Int, fn: (Int, Int) -> Int) {
        let sum = fn(v1,v2)
        print(sum)
    }
    
    exec(v1: 10, v2: 20) { $0 + $1 }
    

    还有一些写法,参看上面--- 具体就不写了

    闭包的循环引用

    下面看看在项目中造成循环引用的代码吧、为什么造成了循环引用和block的造成循环引用一样。
    block捕获了self,self持有block,具体为什么强引用捕获self,这是一个读者自己去想的问题,这里不做过多解释,block很复杂,一两句也说不说完
    上面说的block 就是 闭包

    class ViewController: UIViewController {
        
        var closure:(() -> ())?
        
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view.
            closure = {
                self.view.backgroundColor = UIColor.red
            }
        }
        
         deinit {
            print("ViewController dealloc")
        }
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            closure!()
        }
    }
    

    上面的代码就造成循环引用

    1. 解决循环
        weak var weakSelf = self
        closure = {
            weakSelf?.view.backgroundColor = UIColor.red
        }
    

    和OC中block,解决引用的方法一样

    __weak typeof(self) weakSelf = self; //这里的typeof()只是对括号内的变量进行识别,返回值是变量的类型 = self;
    
    1. swift特有解决循环引用的方式
        closure = { [weak self]
            in self?.view.backgroundColor = UIColor.red
        }
    

    3.是用关键字unowned。

        closure = { [unowned self]
             in self.view.backgroundColor = UIColor.red
        }
    

    推荐使用第二种方式解决闭包循环引用

    逃逸闭包

    1.一个被保存在某个地方等待稍后再调用的闭包就叫做逃逸闭包,通俗点说,闭包在有可能在函数结束后调用,闭包调用逃离了函数的作用域,我们叫这种闭包为逃逸闭包,注意逃逸闭包需要通@escaping关键字声明
    举几个demo

    typealias Fn = () -> ()
    var fn: Fn?
    class Peson {
        func test(lsh: @escaping Fn)  {
            fn = lsh
        }
    }
    var p = Peson()
    p.test {
        print(111)
    }
    fn?()
    
    

    上面的demo闭包lsh被全局变量保存了,等test函数调用完后,在调用闭包,这个比较就是逃逸闭包。

    在看一个例子

    typealias Fn = () -> ()
    class Peson {
        func test(lsh: @escaping Fn)  {
            DispatchQueue.global().async {
                lsh()
            }
        }
    }
    var  p = Peson()
    p.test {
        print(11)
    }
    

    闭包放到异步并发队列中去,也是要等待test函数执行完后,才会执行lsh闭包,这也是逃逸闭包

    1. 逃逸闭包的注意点

    自动闭包

    某个参数用闭包表达式装起来并用@autoclosure声明,就叫自动闭包
    自动闭包的作用:能让代码变得更加漂亮
    举个自动闭包的例子

    func log(ifFalse condition: Bool,
             message: @autoclosure () -> (String),
             file: String = #file, function: String = #function, line: Int = #line)
    {
        guard !condition else { return }
        print("Assertion failed: \(message()), \(file):\(function) (line \(line))")
    }
    
    log(ifFalse: false, message: "111")
    

    自动闭包的注意点
    1.只支持() -> T 格式的闭包
    2.有@autoclosure、无autoclosure构成了函数重载

    相关文章

      网友评论

        本文标题:swift 闭包学习记录

        本文链接:https://www.haomeiwen.com/subject/ztrncctx.html