您的当前位置:首页正文

Swift 内存管理与循环引用问题(weak、unowned)

2024-12-01 来源:个人技术集锦

但是,这种引用计数会产生一个问题就是循环引用:
循环引用

class A {

    var b:B?

    init() { print("A初始化") }

    deinit { print("A析构掉") }

}

class B {

    var a:A?

    init() { print("B初始化") }

    deinit { print("B析构掉") }

}

var a:A?;   a = A()

var b:B?;   b = B()

a!.b = b;   b!.a = a

a = nil;    b = nil

你会发现,A和B的析构函数deinit都没有调用,因为当a执行析构的时候,b.a还在对其进行引用,当b析构的时候,a.b也在对b进行引用。这时候解决的方法就是对其中的某一个声明进行若引用,即加上weak:

weak var b:B?

另外一种造成循环引用的问题就是闭包:闭包中对任何元素的引用都会被闭包自动持有,如果我们在闭包中需要使用self的话,那就相当于闭包对self持有,而block又是被self直接或间接持有,这样就造成了循环引用。例如下面的代码:

class C{

    var name:String

    lazy var block:()->() = {

        print(self.name )

    }

    init(name:String) {

        self.name = name

        print("C初始化")

    }

    deinit {

        print("C析构")

    }

}

var c:C? = C(name:"c")

c?.block()

c = nil

这里C的析构函数也是没有执行的。block是self的属性,block里面又对self持有,这就形成了循环引用。所以这里我们可以使用unowned,也可以使用weak:
//unowned

lazy var block:()->() = {[unowned self] in

    print(self.name)

}
//weak
lazy var block:()->() = {[weak self] in
    if let strongSelf = self{
        print(strongSelf.name)
    }
}

那么这两个使用有什么区别呢?接下来看一个例子:

class C{

    var name:String

    lazy var block:()->() = {[unowned self] in

        print(self.name)

    }

    init(name:String) {

        self.name = name

        print("C初始化")

    }

    deinit {

        print("C析构")

    }

}

class D{

    var block:(()->())!

    init(callBack:(()->())?) {

        self.block = callBack!

        print("D构造")

    }

    deinit {

        print("D析构")

    }

}

var c:C? = C(name:"c")

var d = D.init(callBack:c?.block)

c!.block()

c = nil

d.block()

这里当你运行到 d.block()的时候,是会有一个error。

因为当d.block()执行的时候,c已经被析构掉了,而闭包里的self肯定也是不存在的,是一个nil,这个时候执行的话self.name就会报错。所以在我们不确定是否有外部变量在持有这个block的时候,我们就应该使用weak更为安全,因为使用weak的话self.name需要改成可选性的self?.name,这个时候self?.name肯定就为nil了。所以换成weak之后,在playground里的d.block()就不会有错误了,而且block也是会正常执行的,只不过print(self?.name)打印出来为nil。

欢迎大家访问我的github:,如果有能帮助到大家的地方,可以鼓励我个小星星哦

显示全文