2017-11-03 21 views
6

Tôi muốn tạo một lớp TParent chứa một số đối tượng con bằng cách sử dụng tập hợp. Một số đối tượng là độc lập, trong khi một số có thể cũng phụ thuộc vào các trẻ khác. Tất cả các đối tượng trẻ em phải có một tham chiếu đến phụ huynh. Tôi cũng muốn sử dụng các giao diện nếu có thể.Delphi: Kết hợp đối tượng và rò rỉ bộ nhớ bằng thuộc tính [yếu]

Vì mục đích này, tôi đang sử dụng TInterfacedObject cho TParentTAggregatedObject cho trẻ em. Vì cả hai đứa trẻ và cha mẹ đều biết về nhau, tôi sử dụng các tham chiếu yếu để tránh sự phụ thuộc vào circuar. Thực tế, hành vi này đã được xác định trong TAggregatedObject. Mọi thứ hoạt động tốt khi tôi chỉ sử dụng các đối tượng con độc lập (TIndependantChild).

Sự cố phát sinh khi đối tượng con cũng phụ thuộc vào các trẻ khác, xem hàm tạo của TDependantChild. Tôi lưu trữ các tham chiếu đến một đối tượng con khác trong biến fChild, được đánh dấu bằng attibute [weak], được giới thiệu trong Delphi 10 Berlin. FastMM4 báo cáo rò rỉ bộ nhớ trên shutdown:

enter image description here

Ngoài truy cập vi phạm dẫn đến System.TMonitor.Destroy tăng lương, nhưng điều này chỉ xảy ra khi FastMM4 là trong việc sử dụng và ReportMemoryLeaksOnShutDown là True.

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    FastMM4, 
    System.SysUtils; 

type 
    IParent = interface 
    ['{B11AF925-C62A-4998-855B-268937EF30FB}'] 
    end; 

    IChild = interface 
    ['{15C19A4E-3FF2-4639-8957-F28F0F44F8B4}'] 
    end; 

    TIndependantChild = class(TAggregatedObject, IChild) 
    end; 

    TDependantChild = class(TAggregatedObject, IChild) 
    private 
    [weak] fChild: IChild; 
    public 
    constructor Create(const Controller: IInterface; const AChild: IChild); reintroduce; 
    end; 

    TParent = class(TInterfacedObject, IParent) 
    private 
    fIndependantChild: TIndependantChild; 
    fDependantChild: TDependantChild; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    end; 

{ TParent } 

constructor TParent.Create; 
begin 
    fIndependantChild := TIndependantChild.Create(Self); 
    fDependantChild := TDependantChild.Create(Self, fIndependantChild); 
end; 

destructor TParent.Destroy; 
begin 
    fDependantChild.Free; 
    fIndependantChild.Free; 
    inherited; 
end; 

{ TDependantChild } 

constructor TDependantChild.Create(const Controller: IInterface; const AChild: IChild); 
begin 
    inherited Create(Controller); 
    fChild := AChild; 
end; 

var 
    Owner: IParent; 

begin 
    ReportMemoryLeaksOnShutDown := True; 
    Owner := TParent.Create; 
    Owner := nil; 
end. 

tôi thấy, rằng việc sử dụng [an toàn] thay vì [yếu] giải quyết vấn đề, nhưng theo delphi help

Nó ([an toàn]) nên chỉ được sử dụng bên ngoài đơn vị hệ thống trong những tình huống rất hiếm.

Vì vậy, tôi không tin, tôi nên sử dụng [unsafe] tại đây, đặc biệt là khi tôi không hiểu điều gì xảy ra.

Vì vậy, những gì là những người trấn an các rò rỉ bộ nhớ trong tình huống này và cách khắc phục chúng?

+1

Tại sao trẻ cần được tổng hợp? Bạn có hiểu tổng hợp thực sự là gì không? Tại sao bạn trộn các tham chiếu và giao diện đối tượng? Đó luôn là một công thức cho thảm họa. Sử dụng trình gỡ lỗi để xem lý do bạn đang rò rỉ bộ nhớ. –

+1

'[weak]' được thêm vào cho giao diện ở 10.1 Berlin, nhưng '[weak]' tồn tại trong các phiên bản trước đó. Tôi có thể biên dịch mã của bạn như là trong XE2, nhưng '[yếu]' không có hiệu lực. 'Chủ sở hữu' có' RefCount = 1' vì 'TDependantChild' có tham chiếu không yếu tới' TParent' (do tập hợp) khi 'fChild' được gán, mặc dù là' [yếu] '. Khi 'Owner' bị hủy,' TInterfacedObject.BeforeDestruction() 'gây ra lỗi khi' RefCount <> 0', khiến 'Owner' và con của nó bị rò rỉ. Thay đổi 'fChild' thành' Pointer' sẽ sửa lỗi đó. Tôi nghi ngờ một cái gì đó tương tự trong trường hợp của bạn khi sử dụng '[không an toàn]'. Xác nhận với trình gỡ lỗi –

+0

Remy, Cảm ơn bạn đã nhận xét. Tôi mới đến khái niệm tập hợp, có thể là, tôi hiểu nó sai. Trong trường hợp của tôi, các đối tượng con đại diện cho một số chức năng lớp nhất định. Họ không có sence mà không có cha mẹ, nhưng các đối tượng con tương tự có thể được sử dụng để soạn bố mẹ khác nhau. Tôi không thích trộn giao diện và tham chiếu đối tượng quá, nhưng tôi tìm thấy một số ví dụ nơi nó đã được thực hiện để [link] (https://stackoverflow.com/questions/3483680/delphi-how-delegate-interface-implementation-to-child -object), và nó đưa ra lỗi khi chỉ sử dụng giao diện. Tôi sẽ kiểm tra lại lần thứ hai. – VyPu

Trả lời

4

Vấn đề với rò rỉ và sự cố khi bạn sử dụng trình quản lý bộ nhớ FastMM4 bên ngoài có liên quan đến vấn đề sau liên quan đến việc hoàn tất HashMap nội bộ được sử dụng để theo dõi các tham chiếu yếu.

[REGRESSION XE2/10.1 Berlin] Unable to use 3rd party memory managers

Do vấn đề mà nó là không thể sử dụng quản lý bộ nhớ bên thứ 3 để phát hiện rò rỉ trong Delphi 10.1 và các phiên bản mới hơn, và bao gồm FastMM4 bên ngoài.

Đó là lý do tại sao bạn gặp sự cố với thuộc tính [weak] và bạn không gặp sự cố với [unsafe].


Theo như mã của bạn, bạn có thể sử dụng an toàn [unsafe] trong trường hợp trên. Trong khi có cảnh báo trong tài liệu về cách sử dụng thuộc tính [unsafe], cảnh báo đó thực sự không giải thích tại sao [unsafe] không nên được sử dụng.

Để tạo truyện ngắn, bạn có thể sử dụng thuộc tính [unsafe] khi thời lượng của đối tượng được tham chiếu bởi tham chiếu [unsafe] dài hơn thời gian tồn tại của tham chiếu.

Nói cách khác, bạn phải đảm bảo rằng bạn không truy cập vào tài liệu tham khảo [unsafe] sau khi đối tượng mà đối tượng trỏ đến đã được phát hành và đó là tất cả. Các tham chiếu

[unsafe] không được lấy ra khi đối tượng mà chúng trỏ đến bị hủy và sử dụng tham chiếu sau khi đối tượng đã biến mất sẽ dẫn đến ngoại lệ vi phạm truy cập.

Thay thế thuộc tính [weak] bằng [unsafe] là tất cả những gì bạn phải làm để có mã chức năng đúng như bạn đã trình bày.

TDependantChild = class(TAggregatedObject, IChild) 
    private 
    [unsafe] fChild: IChild; 
+0

Cảm ơn bạn @Dalija cho một câu trả lời rất rõ ràng! Tôi dành nhiều ngày trong khi cố gắng hiểu điều gì xảy ra. Tôi cũng thêm [link] (https://github.com/pleriche/FastMM4/issues/18) vào một cuộc thảo luận liên quan khác mà tôi đã tìm thấy trên liên kết được cung cấp của bạn. Với FastMM4, dự án ví dụ này tạo ra cùng một lỗi vi phạm truy cập như được thảo luận ở đó. – VyPu