2011-09-21 16 views
16

Mẫu sử dụng chính xác cho biến TBytes là gì? Từ sự hiểu biết của tôi, TBytes không phải là một lớp, mà là một "mảng động của các byte". Tôi không chắc chắn nơi bộ nhớ được phân bổ cho nó, khi nó được giải phóng, và đó là cách tốt nhất để chuyển nó từ một nhà sản xuất cho người tiêu dùng. Tôi muốn nhà sản xuất của tôi tạo ra một cá thể TBytes, sau đó chuyển nó cho người tiêu dùng. Sau khi điều này xảy ra, nhà sản xuất muốn sử dụng lại biến thành viên TBytes của nó, nội dung trong kiến ​​thức rằng người tiêu dùng cuối cùng sẽ trả lại bộ nhớ cho hệ thống. Nếu TBytes là một đối tượng, tôi sẽ không gặp bất kỳ vấn đề gì, nhưng tôi không chắc chắn về cách các TBytes hoạt động trong kịch bản này. Ví dụ, trong đối tượng A, tôi muốn tập hợp một số dữ liệu vào một mảng TBytes là một thành viên của đối tượng A. Khi điều đó hoàn tất, sau đó tôi muốn chuyển mảng TBytes đến đối tượng B khác, mà sau đó trở thành chủ sở hữu dữ liệu. Trong khi đó, trở lại đối tượng A, tôi muốn bắt đầu lắp ráp thêm dữ liệu, sử dụng lại biến thành viên TBytes.Delphi XE TBytes sử dụng chính xác

type 
    TClassA = class 
    private 
    FData: TBytes; 
    public 
    procedure AssembleInput(p: Pointer; n: Cardinal); 
    end; 

    TClassB = class 
    public 
    procedure ProcessData(d: TBytes); 
    end; 

var 
    a: TClassA; 
    b: TClassB; 

procedure TClassA.AssembleInput(p: Pointer; n: Cardinal); 
begin 
    SetLength(FData, n); 
    Move(p^, FData, n); // Is this correct? 
    ... 
    b.ProcessData(FData); 

    ... 

    // Would it be legal to reuse FData now? Perhaps by copying new (different) 
    // data into it? 
end; 

procedure TClassB.ProcessData(d: TBytes); 
begin 
    // B used the TBytes here. How does it free them? 
    SetLength(d, 0); // Does this free any dynamic memory behind the scenes? 
end; 

Cảm ơn bạn trước!

Trả lời

13

Mảng động Delphi là các loại được quản lý có quản lý lâu dài tự động. Chúng được tính tham chiếu và khi số lượng tham chiếu đến 0, đã được xử lý. Bạn có thể nghĩ về chúng tương đương với chuỗi, giao diện và biến thể.

Bạn rõ ràng có thể phát hành một tham chiếu đến một mảng động theo một trong ba cách sau:

a := nil; 
Finalize(a); 
SetLength(a, 0); 

Tuy nhiên, nó rất phổ biến chỉ đơn giản là không làm gì cả và để cho các tài liệu tham khảo được phát hành khi biến lá phạm vi.

Một điều cần lưu ý với mảng động là khi bạn có hai tham chiếu đến cùng một mảng động. Trong tình huống đó, các thay đổi được áp dụng qua một tham chiếu có thể nhìn thấy được từ tham chiếu khác vì chỉ có một đối tượng.

SetLength(a, 1); 
a[0] := 42; 
b := a; 
b[0] := 666;//now a[0]=666 

Bạn hỏi nếu điều này là đúng:

Move(p^, FData, n); 

Không nó không phải là. Những gì bạn đã làm ở đây là sao chép nội dung của p vào tham chiếu FData. Nếu bạn muốn sao chép với Move sau đó bạn có thể viết:

Move(p^, Pointer(FData)^, n); 

Hoặc nếu bạn muốn được tiết hơn một chút và tránh những diễn viên bạn có thể viết:

if n>0 then 
    Move(p^, FData[0], n); 

Cá nhân tôi không cảm thấy quá xấu về dàn diễn viên kể từ khi Move hoàn toàn không có an toàn loại nào.


Nó sẽ là pháp lý để tái sử dụng FData bây giờ? Có lẽ bằng cách sao chép dữ liệu mới (khác) vào nó?

Tôi không cảm thấy mình có thể trả lời câu hỏi này mà không cần thêm ngữ cảnh. Ví dụ tôi không biết tại sao FData là một trường vì nó chỉ được sử dụng cục bộ cho hàm đó. Nó sẽ có ý nghĩa hơn như là một biến địa phương. Có lẽ có một lý do nó được khai báo là một trường nhưng nó không thể dễ dàng được phân biệt từ mã này.


Bạn về cách sử dụng mẫu nhà sản xuất/người tiêu dùng. Thông thường, điều này được thực hiện để tách sản xuất khỏi mức tiêu thụ. Tuy nhiên, mã ví dụ của bạn không làm điều này, có lẽ vì mã được tách riêng sẽ quá phức tạp để bao gồm ở đây.

Để thực hiện sản xuất/người tiêu dùng thực sự, bạn cần phải chuyển quyền sở hữu dữ liệu từ nhà sản xuất cho người tiêu dùng. Từ những gì chúng tôi đã mô tả ở trên, một cách rất đơn giản và hiệu quả để làm điều này là sử dụng tính tham chiếu. Khi dữ liệu được chuyển cho người tiêu dùng, nhà sản xuất nên phát hành tham chiếu của nó cho người tiêu dùng.

+0

Cảm ơn bạn đã giải thích. Có, lý do FData là một biến thành viên là vì nhà sản xuất đang lấy dữ liệu từ một socket TCP, vì vậy nó thường chỉ nhận được một phần dữ liệu mỗi khi nó được gọi. Có, tôi muốn nhà sản xuất/người tiêu dùng và nhận xét về "cần phải chuyển quyền sở hữu dữ liệu" là _exactly_ những gì tôi đang cố gắng làm. Và vâng, mã được tách riêng khá phức tạp; do đó đơn giản hóa. Cuối cùng, tôi vẫn không chắc chắn làm thế nào để "chuyển quyền sở hữu" một cách chính xác - bạn có thể làm rõ vấn đề đó một chút? –

+0

Để người tiêu dùng tham khảo và sau đó giải phóng tham chiếu của nhà sản xuất. Consumer.data: = FData; FData: = nil; –

-2

Di chuyển (p ^, FData, n); Đây là OK

quy trình TClassB.ProcessData (d: TBytes); // d là số tham chiếu của FData bắt đầu // d không giữ gì ngoài FData ở nguyên như trước với refcount = 1 // nếu bạn đặt từ khóa "var" trước d, FData sẽ được giải phóng SetLength (d, 0);
kết thúc;

+1

Không, không được. Nó di chuyển byte từ địa chỉ trong p tới con trỏ trong FData. Nó phải là FData [0]. –

4

Có một vài lỗi trong mã của bạn. Nội dung sau sẽ chính xác hơn:

type 
    TClassA = class 
    private 
    FData: TBytes; 
    public 
    procedure AssembleInput(p: Pointer; n: NativeUInt); 
    end; 

    TClassB = class 
    public 
    procedure ProcessData(var d: TBytes); 
    end; 

var 
    a: TClassA; 
    b: TClassB; 

procedure TClassA.AssembleInput(p: Pointer; n: NativeUInt); 
begin 
    SetLength(FData, n); 
    if n <> 0 then Move(p^, FData[0], n); 
    ... 
    b.ProcessData(FData); 
    // FData is ready for reuse here... 
end; 

procedure TClassB.ProcessData(var d: TBytes); 
begin 
    ... 
    SetLength(d, 0); 
end;