2009-09-17 19 views
3

我有一個類實現了一個接口,該接口可用於插件。 類的聲明很簡單。整個應用程序只有這個類的一個實例。當調用返回該接口的函數時,它會在檢索到的接口上調用_AddRef,然後將其作爲結果傳回。不幸的是,它工作,直到我嘗試釋放對象(請參閱「最終化」部分) - 它報告無效指針操作。如果我將它註釋掉,它可以正常工作(但FastMM會報告內存泄漏,所以對象不會被釋放)。Delphi7,傳遞對象的接口 - 釋放對象時導致無效指針操作

這是返回接口的函數中的代碼部分(實際上它是我的「服務管理器」類的重載QueryInterface)。

if ConfigManager.GetInterface(IID, obj) then 
begin 
    ISDK_ConfigManager(obj)._AddRef; 
    result:= 0; 
end 

和CONFIGMANAGER類的代碼...

type 
    TConfigManager = class(TInterfacedObject, ISDK_ConfigManager) 
    private 
    ... 
    end; 

var 
    ConfigManager: TConfigManager; 
implementation 

... 

initialization 
    ConfigManager:= TConfigManager.Create(); 
finalization 
    if ConfigManager <> nil then 
    FreeAndNil(ConfigManager); //if I comment it out, it leaks the memory but no Invalid Ptr. Op. raises 

我在做什麼錯? 我需要通過一個參考這個 ConfigManager的實例。

+0

TConfigManager.Destroy是做什麼的? – Ozan 2009-09-17 00:58:11

+0

什麼都不會導致錯誤。確保我在發佈之前評論過它,並沒有幫助我... – migajek 2009-09-17 01:14:21

+0

好的我發現*解決方法*,而不是*修復*。而不是調用FreeAndNil,我打電話._release ...但仍然在尋找答案! – migajek 2009-09-17 01:15:00

回答

10

您在處理接口時會聽到的第一條建議是從不將接口引用與對象引用混合在一起。這意味着,一旦你通過接口引用開始引用對象,就不再通過對象引用來引用對象。永遠。

原因是,第一次分配接口變量時,對象的引用計數將變爲1.當該變量超出作用域或被賦予新值時,引用計數變爲零,並且對象釋放自己。這一切都沒有對原始對象引用變量進行任何修改,因此當您稍後嘗試使用該變量時,它不是空指針,但它引用的對象已消失 - 這是一個懸掛引用。當你嘗試釋放一些不存在的東西時,你會得到一個無效的指針操作異常。

聲明你的ConfigManager變量爲接口。不要自己釋放它。一旦你這樣做了,你可以將整個聲明TConfigManager移動到實現部分,因爲該單元之外的任何代碼都不會引用它。

此外,很少有任何理由提供您自己的QueryInterface的實施。 (你說你取代了它,但那是不可能的,因爲它不是虛擬的。)由TInterfacedObject提供的那個應該是足夠的。你提供的實際上是導致內存泄漏,因爲你不應該增加引用計數。 GetInterface已經調用_AddRef(通過執行接口分配),所以你要返回帶有充足參考計數的對象。

+0

ad 1. ok,以及如果我希望我的主窗體實現其中一個接口,並且在插件請求時傳遞,該怎麼辦? 順便說一句我有它以相同的方式實現(Form.GetInterface ... + _AddRef),它在那裏工作! ad 2.right,不是真的壓倒它,只是使用「自己」的版本;) 爲什麼? 因爲我傳遞給插件的接口是「服務」,這是插件可以訪問的唯一東西,而且非常有限,不會公開任何函數。 然而後,它可以被用來訪問各種接口,可與代碼likie ' (FApplication爲ISDK_ConfigManager).DoSomething ... ' – migajek 2009-09-17 01:03:44

+0

在形式,'_AddRef'並沒有真正做任何事情。我不明白你有自己實現'QueryInterface'的理由。 *所有*接口提供'QueryInterface',即使你的「服務」接口,你說沒有公開任何功能。 (如果它沒有任何功能,那麼爲什麼它存在?) – 2009-09-17 01:25:04

+0

它存在爲所有可用的服務提供接口,如給出的例子;) – migajek 2009-09-17 08:38:32

2

你說這是一個插件系統?你是否將你的插件加載爲BPL?實際上,我上週遇到了這個問題。你不能依靠定稿清除你的接口引用。在卸載插件之前,您需要確保將其清除,否則其內存空間變得無效。

編輯:通過「清除接口引用」我的意思是調用_Release對其進行調用,可以通過手動將其設置爲零或讓引用超出範圍。如果你的界面管理器持有對插件的接口引用,當界面管理器被破壞時它們將被清除。

+0

nope,插件是DLL,但他們都(應用程序和插件)使用共享的BPL。你能告訴我你通過清除ifaces引用是什麼意思嗎? 順便說一句,你是如何編輯我的問題突出語法? :P我沒有在StackOverflow上得到這個編輯器,它不能爲我處理< code >標記... – migajek 2009-09-17 00:50:59

+0

StackOverflow不能使用< code >標記執行代碼視圖。它是通過特殊的縮進來實現的,或者如果您在段落中嵌入代碼則反引號。我通過刪除標籤,突出顯示代碼並按下編輯器上方的「010 101」按鈕來修復它,該編輯器會自動設置格式。 – 2009-09-17 01:21:50

1

我完全同意羅布。

最有可能幫助的是重寫您的初始化代碼,如下所示。

現在ConfigManager的類型爲ISDK_ConfigManager,並且通過給它賦值nil,引用計數將遞減。 當引用計數變爲零時,它將自動釋放。

type 
    TConfigManager = class(TInterfacedObject, ISDK_ConfigManager) 
    private 
    ... 
    end; 

var 
    ConfigManager: ISDK_ConfigManager; 
implementation 

... 

initialization 
    ConfigManager:= TConfigManager.Create(); 
finalization 
    ConfigManager := nil; 
end; 

--jeroen

0

確實TConfigManager類已宣佈爲 「發佈」 的任何方法?