swift 闭包学习记录

在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. 闭包的语法糖调用,用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, 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 闭包学习记录)