2016-06-19 39 views
5

mam ten kod (muszę dodać obiekt string do TStringList):Czy muszę zwolnić BSTR (WideString) przydzielony przy pomocy SysAllocString?

var 
    WS: WideString; 
begin 
    WS := 'allocated string'; 
    SL.AddObject('my string', TObject(SysAllocString(PWideChar(WS)))); 

A później ją przeczytać:

var 
    WS: WideString; 
begin 
    WS := PWideChar(SL.Objects[0]); 
    ShowMessage(WS); 

Zastanawiałem się, czy system będzie dbać o BSTR, która była przydzielone z SysAllocString. lub czy muszę zadzwonić pod numer SysFreeString? nie wynika to z dokumentacji.

Teraz, jeśli system nie De-alokuje go, czy jest jakiś sposób, aby to udowodnić?

P.S: Rzeczywiście, jest suffucient zadzwonić:

SL.AddObject('my string', TObject(PWideChar(WS))); 

Bez użycia SysAllocString. (i nie rozumiem, jak to działa)

Odpowiedz

6

Tutaj następujący wiersz przydziela nowy BSTR i wypełnia jego wskaźnik do wskaźnika SL.Objects[].

SL.AddObject('my string', TObject(SysAllocString(PWideChar(WS)))); 

Więc po będzie ostatecznie wyciek pamięci:

var 
    WS: WideString; 
begin 
    WS := PWideChar(SL.Objects[0]); 

Tutaj nowy WS instancja zostanie rozdzielona, ​​więc instancja BSTR wskazanych przez SL.Objects[0] nie zostanie zwolniony.

I dodaje pracuje przez przypadek:

SL.AddObject('my string', TObject(PWideChar(WS))); 

pamięci wskazywanej przez bufor pamięci jest nadal PWideChar(WS) zawierające do poprzedniego WS: WideString instancji. Tak więc może działać ... dopóki bufor nie zostanie ponownie użyty i nadpisany przez inne dane, a inny tekst zostanie zwrócony lub wystąpi losowy GPF.

Za radą: nigdy nie oszukuj systemu typu Delphi, przechowując coś innego niż TObject w zmiennej wpisanej jako TObject ... chyba że wiesz, co robisz. Nie baw się wskazówkami, dopóki nie wiesz, czym one są i jak działają.

Nie widzę żadnej korzyści z przechowywania WideString w obrębie wpisu !Zmień strukturę danych: stwórz prawdziwy class, przechowując swój ciąg. Wtedy wszystko będzie jasne i czyste:

type 
    TMyStoreWS = class 
    protected 
    fText: WideString; 
    public 
    constructor Create(const aText: WideString); virtual; 
    property Text: WideString read fText write fText; 
    end; 

constructor TMyStoreWS.Create(const aText: WideString); 
begin 
    inherited Create; 
    fText := aText; 
end; 

... 
SL.AddObject('my string', TMyStoreWS.Create(aText)); // clean 
... 
ShowMessage(SL.Objects[0].Text); // clean 
SL.Objects[0].Free; // don't forget to release 

Mała narzut alokacji instancji class jest negligeable w odniesieniu do BSTR alokacji strun, mogę ci powiedzieć. Twój kod byłby zdecydowanie czystszy i łatwiejszy do utrzymania/ewolucji.

+0

Miałem nadzieję, że pozwolę systemowi zarządzać zniszczeniem "obiektu" WideString. jeśli używam struktury klasy, muszę ręcznie zwolnić każdy obiekt (nie ma OwnsObject w D7 dla TStringList). Nadal nie jestem pewien, czy ** musi ** wolne ciągi, które są przydzielone z 'SysAllocString' lub system może zająć się tym dla mnie? ... – zig

+0

Tak, ** musisz ** darmowe ciągi BSTR przydzielone przez SysAllocString. –

2

Delphi zwolni WideString, gdy tylko wykracza poza zakres.
Ponieważ WideString jest typem zarządzanym.
Jednak jeśli rzucisz najszerszy segment na PWideChar, Delphi nie policzy tego jako referencji, a zatem zniszczy ciąg znaków, gdy tylko funkcja się zakończy, nawet jeśli nadal istnieje odniesienie do niej.

To źle, ponieważ teraz masz zwisający wskaźnik. Dlatego potrzebujesz SysAllocString.

To, co robi SysAllocString, tworzy kopię napisanego ciągu. Ta kopia nie jest zarządzana, więc musisz ją zniszczyć samodzielnie, używając SysFreeString.