2017-07-14 66 views
0

在應用程序中,我想知道爲什麼在退出應用程序時不會調用類的deinit方法的實例。瞭解AppDelegate中的保留數

作爲一個例子,這裏介紹的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") 
    } 
} 

這不僅無法撥打測試的方法deinit,但在測試的init是2擋計數;我本來期望這是1

如果一個可選的引用存儲在AppDelegate類和創建測試實例時設置,它是零,當applicationWillTerminate被稱爲

是否有人可以解釋爲什麼保留計數在這裏是2,以及如何確保在應用程序終止時調用Test的deinit?

+0

你是什麼意思的「可選」參考。你的意思是一個Swift可選類型還是你的意思是一個弱引用? –

+0

@AllenHumphreys,抱歉不清楚,這是一個可選類型和一個弱引用的結果。 – TheDarkKnight

回答

0

問題是由於從init終止應用程序在Test類的。我懷疑在init調用terminate阻止類的正確的實例,因此其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") 
    } 
} 
1

我不能,爲什麼保留計數講話爲2。在一般情況下,啓用ARC,你真的不應該永遠檢查保留計數,而原因是well answered question

此外,還有一個answer,暗示CFGetRetainCount實際上可能會增加保留計數,當你調用它。

在您的情況下,deinit未被調用,因爲您在初始化程序完成之前以編程方式終止了應用程序。

在具有分配給一個變量Test實例的情況在你AppDelegatedeinit不叫,因爲AppDelegate不是在「正常」的方式,當一個應用程序退出釋放。因此,它的所有屬性都不會被釋放。如果您在applicationWillTerminate中將變量設置爲nil,您將看到deinit被恰當調用。在討論this answer中的全局變量時解釋了這種行爲。

爲了提供您所提供的例子的具體排列:

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 
    } 
} 

deinit只能保證如果實例是發佈由ARC被調用,但是如果釋放不會被調用,舉例來說,如果應用程序崩潰或者是用戶非常強迫它不會那麼,不要依靠它來進行絕對關鍵的「清理」。

回覆保留計數。您的代碼,執行完全一樣是你的問題,產生如下:

enter image description here

我們所看到的是,保留計數由一個在通話過程中增加爲CFGetRetainCount,然後遞減它返回時,然後在傳遞到terminate時再次遞增。

+0

Cmd + q產生相同的結果; deinit不會被調用,並且程序正在乾淨地終止。 – TheDarkKnight

+0

是的,現在我已經將它作爲應用程序委託的一個屬性,我發現它並沒有被稱爲 –

+0

它真的令人沮喪,因爲它打破了語言語義的期望,所以我想這意味着我們不能用它來清理一個類,如果我們認爲該過程可以退出並且必須以其他方式處理它。 – TheDarkKnight

1

我假定夫特情況是一樣的Objective-C的一個在此鏈接記載: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-BAJHFBGH

「當應用程序終止時,對象可以不被髮送的的dealloc消息。因爲進程的內存在退出時自動清除,所以僅允許操作系統清理資源比調用所有內存管理方法效率更高。「

+0

這很有道理,雖然很煩人,因爲它打破了語言語義的期望;至少它是記錄在案的,謝謝指出。 – TheDarkKnight