了解AppDelegate中的保留计数

时间:2017-07-14 14:37:42

标签: swift macos memory-leaks retain

在一个应用程序中,我想知道为什么在退出应用程序时没有调用类的deinit方法的实例。

举个例子,这里介绍的Test类是在AppDelegate的applicationDidFinishLaunching中创建的。

import Cocoa

class Test {
    let testVar = 1

    init() {

        print("Retain count \(CFGetRetainCount(self))")
        NSApplication.shared().terminate(self)
    }

    deinit {
        print("Calling deinit")
    }
}


@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    //@IBOutlet weak var window: NSWindow!    

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application

        _ = Test()  
    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application            

        print("Terminating")
    }
}

这不仅无法调用Test的deinit方法,而且Test init中的保留计数为2;我原以为这是1。

如果可选引用存储在AppDelegate类中并在创建Test实例时设置,则在调用applicationWillTerminate时为nil

有人可以在这里解释为什么保留计数为2以及如何确保在应用程序终止时调用Test的deinit?

3 个答案:

答案 0 :(得分:1)

我无法说明为什么保留计数为2.一般来说,启用ARC后,你真的不应该检查保留计数,原因是well answered question < / p>

此外,还有answer表示CFGetRetainCount实际上可能会在您调用时增加保留计数。

在您的情况下,deinit未被调用,因为您在初始化程序完成之前以编程方式终止应用程序。

如果Test上的变量已分配AppDelegate,则deinit未调用AppDelegate,因为nil未在&#34中发布;正常&#34;应用程序退出时的方式。因此,它的所有属性都不会被释放。如果您在applicationWillTerminate中将变量设置为deinit,那么您会看到class Test { let testVar = 1 init() { print("Retain count \(CFGetRetainCount(self))") } deinit { print("Calling deinit") } } @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { var variable: Test? func applicationDidFinishLaunching(_ aNotification: Notification) { variable = Test() DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) { // If you're trying to pass in `variable`, the terminate funciton will retain it NSApplication.shared.terminate(nil) } } func applicationWillTerminate(_ aNotification: Notification) { variable = nil } } 被恰当地调用。在this answer中讨论全局变量时,会解释此行为。

提供您提供的示例的特定排列:

deinit
如果实例是由ARC发布的,那么

CFGetRetainCount只能被保证被调用,但是如果从未调用过版本,例如,如果应用程序崩溃或被用户强行使用它就不会被所以,不要依赖它来绝对批判&#34;清理&#34;。

重新考虑保留计数。您的代码完全按照您的问题执行,产生以下内容:

enter image description here

我们所看到的是保留计数在调用terminate期间递增1,然后在返回时递减,然后在它传递给{{时再次递增1}}。

答案 1 :(得分:1)

我认为Swift情况与此链接中记录的Objective-C相同: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH

&#34;当应用程序终止时,可能不会向对象发送dealloc消息。因为进程的内存在退出时自动清除,所以只是允许操作系统清理资源比调用所有内存管理方法更有效。&#34;

答案 2 :(得分:0)

问题是由于initTest内的应用程序终止。我怀疑在terminate中调用init会阻止正确实例化类,因此永远不会调用它deinit

通过延迟拨打terminate,可以按预期调用对测试版deinit的调用

import Cocoa

class Test {

    init() {

        DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
            NSApplication.shared().terminate(self)
        }
    }

    deinit {
        print ("Calling Deinit")
    }
}


@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    //@IBOutlet weak var window: NSWindow!
    var variable: Test?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application

        variable = Test()

    }

    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application

        variable = nil
        print("Terminating")
    }
}