自动引用计数(ARC)的实际应用

6

我目前正在遵循苹果文档。这是我的问题:

class Person {
    let name: String
    init(name: String) {
        self.name = name
        println("\(name) is being initialized")
    }
    deinit {
        println("\(name) is being deinitialized")
    }
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        var reference1: Person?
        var reference2: Person?
        var reference3: Person?
        reference1 = Person(name: "John Appleseed")
        // prints "John Appleseed is being initialized
        reference2 = reference1
        reference3 = reference1

        reference1 = nil
        reference2 = nil
    }
}

reference1等于nil之后,ARC会释放该实例并打印"John Appleseed is being deinitialized"

难道不应该在reference3 = nil之后释放吗?


@JakubVano 为了获取更多信息,我在viewDidLoad()方法中运行此代码。然而,即使存在Strong引用,编译器也会释放实例,导致reference3不是nil。 - ridvankucuk
那个截图增加了混乱,而不是清晰度。 - nhgrif
你是如何确定 deinit 实际发生的精确时刻的?我期望它会在方法返回的同时被调用(绝对不会晚一会儿)。由于某种优化,它可能会早些被调用,但它绝对不会在方法返回后停留。 - nhgrif
如果我错了,请纠正我,Person实例有3个强引用。我通过将它们设置为nil来中断了2个引用。但是仍然有1个强引用。编译器如何调用deinit方法? - ridvankucuk
因为第三个变量一旦超出其作用域,也就是viewDidLoad方法返回时,就会被设置为nil - nhgrif
我添加了一些断点,你是对的,deinit在viewDidLoad方法返回后发生。 - ridvankucuk
2个回答

6
问题在于范围的问题。我们可以通过断点来了解更多情况。
在这里,我们停在初始化"reference1"之前。所有变量都是预期的"nil":
图片:https://istack.dev59.com/lLktX.webp 那么在"reference1"初始化之后呢?
图片:https://istack.dev59.com/J9TiK.webp 好的,让我们跳到"reference2"和"reference3"设置之后:
图片:https://istack.dev59.com/wthqi.webp 所有三个变量指向同一内存位置,我们可以看到初始化程序只运行了一次。它们都指向同一位置。
让我们再往前走:
图片:https://istack.dev59.com/7MayA.webp "reference1"现在指向"None",也就是"nil"。"deinit"方法还没有被调用并打印出它的消息。
让我们再往前走一些:
图片:https://istack.dev59.com/HGED6.webp 现在"reference1"和"reference2"都是预期的"nil"。我添加的"println"语句已经被调用。但是"deinit"没有运行,"reference3"也不是"nil"。
下一步是完全退出这个方法。一旦我们退出这个方法,变量就超出了范围,"deinit"就会被调用:
图片:https://istack.dev59.com/kswkH.webp

1
编译器能够识别到局部创建的对象不再被使用,因此进行了解除分配。例如,在下面的代码中,在执行viewDidLoad方法后,Person将被解除分配,因为没有人会在未来使用person对象。
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.\

        var reference1: Person?
        var reference2: Person?
        var reference3: Person?
        reference1 = Person(name: "John Appleseed")
        // prints "John Appleseed is being initialized
        reference2 = reference1
        reference3 = reference1

        reference1 = nil
        reference2 = nil

//        println(reference3?.name)
}

如果你想保留对象,那么你需要按照以下方式将reference3创建为property:
class ViewController: UIViewController {

    var reference3 : Person?

    override func viewDidLoad() {
        super.viewDidLoad()

        var reference1: Person?
        var reference2: Person?

        reference1 = Person(name: "John Appleseed")
        // prints "John Appleseed is being initialized
        reference2 = reference1
        reference3 = reference1

        reference1 = nil
        reference2 = nil

//        println(reference3?.name)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        println(reference3?.name)
    }

}

即使 reference3 不是全局变量,我仍然可以访问 reference3?.name - ridvankucuk
如果你在didLoad中声明reference3并在didAppear中使用它,那么应用程序将无法编译。 - kmithi1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接