繁体   English   中英

如果他们引用的对象被释放,守卫会让`self` = self 在使用[weak self] 的闭包中导致崩溃吗?

[英]Could a guard let `self` = self inside a closure that uses [weak self] cause a crash if the object they reference becomes deallocated?

我查看了一些关于使用 [weak self] 和 [unowned self] 堆栈溢出问题的评论。 我需要确保我理解正确。

我正在使用最新的 Xcode - Xcode 13.4,最新的 macOS - macOS Monterey 12.4,并且我正在编写与 iOS 12.0 兼容到最新 iOS 15 的代码。

首先,我是否纠正了对它有强引用的内存由强引用所属的任何一个拥有?

另外,这是我真正需要知道的,我想知道如果我在闭包的开头使用[weak self] in会发生什么,然后我有语句guard let `self` = self else { return } . 我相信保护语句,如果条件成功,将引用 self 分配给在保护语句的let `self部分中新声明的名为 self 的强引用。 那是对的吗?

让我问的是,关闭结束时会发生什么。 我是否正确,即使较新的 self 拥有强引用,一旦闭包中的代码执行了闭包中的最后一条语句,强引用指向的内存就会被释放,因为具有强引用的较新的 self 在闭包本身,并与为该闭包分配的所有内存一起被释放。

我想我做对了这一切。 如果没有,请告诉我。

如果您愿意,我对任何其他信息或任何澄清感兴趣。

如果闭包持有强引用,只要闭包仍然可以访问,它就会阻止 ARC 释放对象。

weakunowned的自我允许解决这种强大的所有权
但这意味着闭包的弱自身引用的对象可以被取消初始化,而闭包仍然可以被调用。 在这种情况下,闭包的 self 将引用 nil。

guard实际上只是检查引用是否仍然有效。 无论如何,一个 nil 都会让它失败。 如果它是一个有效的参考,它不会改变捕获列表; 您可以假设 self 将保持有效,直到当前执行的闭包返回。

实验证明

这里有一些代码来试验差异,带有一个跟踪初始化和取消初始化的类,并且可以返回一个带有self捕获的闭包:

class T {
    var id:String
    init(_ name:String){
        id = name
        print ("INIT T object \(id)")
    }
    func getClosure() -> (Int)->Int {
        return { [weak self] (i:Int) in    
            guard let self = self else {
                print ("Instance vanished")
                return -1
            }
            print (i,self.id)
            return 0
        }
    }
    deinit {
        print ("DEINIT T object \(id)")
    }
}

让我们创建一个戏剧性的环境,使闭包可以在创建它的实例中存活:

var oops:(Int)->Int = {(Int)->Int in 0}

func f1() {
    var t = T("f1_test")
    t.getClosure()(32)       // closure is disposed at the end of the function
}                            // t will be deinitialized in any case
func f2() {
    var t = T("f2_test")
    oops = t.getClosure()    // closure will be kept outside of scope
}                            // t can be disposed only if capture is not strong

f1()
f2()
oops(33)

让我们分析一下会发生什么。 f1()将导致T实例将被取消初始化。 这是因为离开f1时不再引用闭包,所以捕获的self也不再被引用,ARC 将终止它:

INIT T object f1_test
32 f1_test
DEINIT T object f1_test

f2()调用更加微妙,因为T实例在离开f2时仍然被闭包引用,并且闭包在全局变量中存在。

如果捕获很强大,则不需要guard ,因为 ARC 会确保实例仍然可用。 您会看到 T 实例不会被取消初始化。

但是由于捕获是weak的,从f2返回时没有对T实例的强引用,它会被正确地取消初始化。 所以守卫会让你得到:

INIT T object f2_test
DEINIT T object f2_test
Instance vanished

这证明了guard let self = self并没有将闭包的弱捕获替换为强捕获。

当您编写这样的闭包时:

{ [weak self] in
    guard let self = self else { return }
    ...
}

您正在检查声明此闭包的类是否仍被分配,如果该类仍被分配,您将在闭包本身内创建对它的新强引用。

您这样做是因为如果删除了对该类的所有其他强引用,那么仍然会在

guard let self = self else { return }

此时,您可以确定在闭包结束之前,声明闭包的类被分配,因为引用在闭包本身中。

所以,回答你的问题,不,如果你写

guard let self = self else { return }

该关闭不会崩溃,因为您有一个仍然存在的强引用。

不同的是,如果您使用 [unowned self]。 Unowned self 就像一个未包装的可选项。 如果你使用它而不检查它的存在,你就是在隐式地写

self!.<something>

因此,如果在调用该闭包时释放了 self ,它将崩溃。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM