請有人解釋一下它是如何作業的?我認為 TObject 和 String 不兼容,它不應該作業。
var
xObj: TObject;
begin
// xObj := TObject('yyy'); // invalid typecast
xObj := TObject('yyy ' ClassName); // no error
ShowMessage(String(xObj)); // no error too
end;
我也很好奇 xObj 的記憶體什么時候會被釋放?
uj5u.com熱心網友回復:
這是一個很難回答的問題,因為
如果您知道 Delphi 內部資料格式和記憶體管理是如何作業的,那么這個問題就很簡單了。
如果您不知道 Delphi 內部資料格式和記憶體管理是如何作業的,那么一個單一的 SO 答案無法教給您。
但是,由于非常天真,我將為您做一個非常簡短的介紹。本簡介包含一些檔案鏈接。我希望你會閱讀那些檔案頁面。我還使用了很多技術詞匯。我希望你會努力真正理解這些概念。
在 Delphi 中,物件變數只不過是指向物件的指標。因此,它是一個原生大小的整數。例如,在 Win64 上,它是一個 64 位整數——計算機記憶體中物件的地址。
如果A, B: TBitmap你這樣做A := B了,那么只有這樣一個整數被復制,所以A然后B指向堆上的同一個位圖物件(或者都是nil指標)。換句話說,NativeInt(B) = NativeInt(A).
物件(如果我們暫時忘記介面物件)不是由編譯器管理的。您必須手動創建它們,并且必須手動釋放它們:
var Frog := TFrog.Create;
try
// Use Frog
finally
Frog.Free;
end;
顯然,您必須非常小心,不要造成記憶體泄漏或使用懸空指標。始終使用try..finally成語(及其朋友),就像您的生活依賴于它一樣。
類似地,型別變數string是指向字串堆物件的指標。因此,字串變數只不過是一個原生大小的整數。例如,在 Win64 上,它是一個 64 位整數——計算機記憶體中字串堆物件的地址。
像物件一樣,字串也是參考型別。但是,與物件不同,它們是由編譯器進行參考計數和管理的,因此您永遠不需要自己創建或釋放它們。
盡管字串是參考型別,但它們“看起來”像值型別,因為它們使用寫時復制 (COW) 語意。
因此,在字串變數賦值中,如
b := a;
該賦值將 (1)b指向與a(以便之后NativeInt(b) = NativeInt(a)) 相同的字串堆物件,以及 (2)a通過將其參考計數增加 1 來更改字串堆物件,因為 nowb也指向它。此外,b曾經指向的堆物件(如果有的話)將其參考計數減一,因為b不再指向它。如果它達到零,則釋放堆物件。
Now, if A is an object and s a string, then both A and s are nothing but native-sized integers, so of course you can tell the compiler to perform the assignment.
However, this is (almost always) a horribly bad idea. If you use string(X), where X is a non-nil pointer that doesn't point to a string heap object, the compiler will still treat this part of memory as a string. Then horrible things will happen, like memory corruption (if you are unlucky) and AVs (if you are very lucky).
Similarly, if s is a string and you start to work with TBitmap(s), the compiler will treat the memory that s points to (a string heap object) like a TBitmap instance. Again, horrible things will happen.
In fact, horrible things may happen even if you only (seemingly) "read" from an incorrectly typed pointer, if you mix managed and non-managed types. For instance, at end, perhaps the compiler will add code to reduce the reference count of a "string" variable that doesn't point to a string heap object.
Or, relevant to the OP's actual case: a string may be freed behind your back.
Consider this program:
var
p: Pointer;
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
s := Random(1024).ToString;
p := Pointer(s);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(string(p));
end;
When you click Button1, a local string variable s will be set to point to a random number string heap object with reference count 1. Then we save the address to this string heap object in a non-managed pointer variable.
When we hit the end of Button1Click, the reference count of this heap object drops to 0, so it is freed. Hence, p is now a dangling pointer.
So if you press Button2, you will be accessing a dangling pointer: p doesn't point to string heap object, but to some memory you don't have any control over.
You may get the old contents of the string. Or something completely different. Or an AV.
Hint: Set a breakpoint on p := Pointer(s); and go to the Memory panel (Ctrl Alt 1). Press Ctrl G there and type s[1] to take you to the string. At negative offset, you will find the reference count of the heap object. As you step over end, you will see that it drops to zero.
Bonus material: Dynamic arrays are reference types that are reference counted and managed by the compiler, but that do not use COW semantics. [Actually, on my YouTube channel you can find a not horribly bad video about their internal machinery.]
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/424534.html
標籤:德尔福
上一篇:如何將類方法傳遞給SetWindowLongPtr()?
下一篇:如何從磁盤獲取SMART資訊?
