swift 闭包循环引用

209 阅读1分钟
  • 闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
class Dog {
	var fn: (() -> ())?
    func run() {
    	print("run")
    }
    
    deinit {
    	print("deinit")
    }
}

func test() {
 let d = Dog()
 d.fn = {
 	d.run()
 }
}

test()

此时dog对象指向了闭包,而闭包也指向了dog对象,造成循环引用,导致dog对象无法释放(deinit不会调用)

  • 在闭包表达式的捕获列表声明weakunowned引用,解决循环引用
// 捕获列表 [], [weak p], 无参数列表
d.fn = {
	[weak p] in
    d?.run()
}
class Dog {
	var fn: ((Int) -> ())?
    func run() {
    	print("run")
    }
    
    deinit {
    	print("deinit")
    }
}

func test() {
 let d = Dog()
 d.fn = {
 	d.run()
 }
}

test()
// 捕获列表 [], [weak p], 有参数列表
d.fn = {
	// 也可 [weak weakSelf = p]
	[weak p](age) in
    d?.run()
}
  • @escaping 一般是作为参数传递给函数
// 非逃逸闭包:闭包调用发生在函数结束前, 闭包调用在函数作用域内
func test(_ fn: () -> ()) {
	fn()
}

test {
	print("test")
}
// 逃逸闭包:闭包有可能在函数结束后调用, 闭包调用逃离了函数作用域,此时需要@escaping声明

import Dispatch
typealias Fn = () -> ()
class Person {
	var fn: Fn?
    
    init(fn: @escaping Fn) {
    	self.fn = fn
    }
    
    func test() {
		// DispatchQueue.global().async也是一个逃逸闭包
        // 因为用到了实例成员(属性、方法),编译器会强制要求明确写出self, 此处没有循环引用
        DispatchQueue.global().async {
        	[weak weakSelf = self] in
        	weakSelf?.fn()
        }
        // 也可
        DispatchQueue.global().async {
        	self.fn()
        }
	}
}