17

đây là ví dụ được tạo. Tôi không muốn đăng mã gốc ở đây. Tôi đã cố gắng trích xuất các phần liên quan.Giao diện, Phương thức ẩn danh và Rò rỉ bộ nhớ

Tôi có giao diện quản lý danh sách người nghe.

TListenerProc = reference to procedure (SomeInt : ISomeInterface); 

ISomeInterface = interface 
    procedure AddListener (Proc : TListenerProc); 
end; 

Bây giờ tôi đăng ký một người biết lắng nghe:

SomeObj.AddListener (MyListener); 

procedure MyListener (SomeInt : ISomeInterface); 
begin 
    ExecuteSynchronized (procedure 
         begin 
         DoSomething (SomeInt); 
         end); 
end; 

tôi làm được rò rỉ bộ nhớ. Cả hai phương thức nặc danh và các giao diện đều không bao giờ được giải phóng. Tôi nghi ngờ rằng điều này là do một số loại tham chiếu vòng tròn ở đây. Phương thức nặc danh giữ cho alife giao diện và giao diện giữ cho alife ẩn danh.

Hai câu hỏi:

  1. Bạn có hỗ trợ giải thích điều đó không? Hay tôi còn thiếu cái gì khác ở đây?
  2. Có điều gì tôi có thể làm được không?

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


EDIT: Đó không phải là dễ dàng như vậy để tái sản xuất này trong một ứng dụng đủ nhỏ để gửi nó ở đây. Điều tốt nhất tôi có thể làm bây giờ là như sau. Phương thức ẩn danh không được phát hành tại đây:

program TestMemLeak; 

{$APPTYPE CONSOLE} 

uses 
    Generics.Collections, SysUtils; 

type 
    ISomeInterface = interface; 
    TListenerProc = reference to procedure (SomeInt : ISomeInterface); 

    ISomeInterface = interface 
    ['{DB5A336B-3F79-4059-8933-27699203D1B6}'] 
    procedure AddListener (Proc : TListenerProc); 
    procedure NotifyListeners; 
    procedure Test; 
    end; 

    TSomeInterface = class (TInterfacedObject, ISomeInterface) 
    strict private 
    FListeners   : TList <TListenerProc>; 
    protected 
    procedure AddListener (Proc : TListenerProc); 
    procedure NotifyListeners; 
    procedure Test; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    end; 


procedure TSomeInterface.AddListener(Proc: TListenerProc); 
begin 
FListeners.Add (Proc); 
end; 

constructor TSomeInterface.Create; 
begin 
FListeners := TList <TListenerProc>.Create; 
end; 

destructor TSomeInterface.Destroy; 
begin 
FreeAndNil (FListeners); 
    inherited; 
end; 

procedure TSomeInterface.NotifyListeners; 

var 
    Listener : TListenerProc; 

begin 
for Listener in FListeners do 
    Listener (Self); 
end; 

procedure TSomeInterface.Test; 
begin 
// do nothing 
end; 

procedure Execute (Proc : TProc); 

begin 
Proc; 
end; 

procedure MyListener (SomeInt : ISomeInterface); 
begin 
Execute (procedure 
     begin 
     SomeInt.Test; 
     end); 
end; 

var 
    Obj  : ISomeInterface; 

begin 
    try 
    ReportMemoryLeaksOnShutdown := True; 
    Obj := TSomeInterface.Create; 
    Obj.AddListener (MyListener); 
    Obj.NotifyListeners; 
    Obj := nil; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 
+0

Bạn nên cho chúng tôi biết cách hoạt động của AddListener. –

+0

Tôi chỉ cần đặt chúng trong một 'TList . ' – jpfollenius

+1

Tất cả mã tôi nhìn thấy có vẻ tốt. Vấn đề phải nằm trong phần ẩn. Bạn có thể hiển thị một ví dụ hoàn chỉnh tạo ra sự rò rỉ không? –

Trả lời

8

Mã của bạn ở mức tối thiểu. Sau đây:

program AnonymousMemLeak; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

type 
    TListenerProc = reference to procedure (SomeInt : IInterface); 

procedure MyListener (SomeInt : IInterface); 
begin 
end; 

var 
    Listener: TListenerProc; 

begin 
    try 
    ReportMemoryLeaksOnShutdown := True; 

    Listener := MyListener; 
    Listener := nil; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end. 

có cùng một vấn đề (Delphi 2009 ở đây). Điều này không thể được làm việc hoặc thiết kế xung quanh. Trông với tôi như một lỗi trong trình biên dịch.

Edit:

Hoặc có lẽ đây là một vấn đề về việc phát hiện rò rỉ bộ nhớ. Nó không có gì để làm với tham số là một giao diện, một thủ tục không tham số dẫn đến cùng một "rò rỉ". Rất lạ.

+0

Điều đó có vẻ là một vấn đề trình biên dịch. Nếu bạn di chuyển mã (khối thử) từ thủ tục chính của chương trình đến một thủ tục và sau đó có cuộc gọi chính là thủ tục, không có rò rỉ nào được báo cáo. –

+0

OK, hãy thử mã mẫu quá dài. Tôi nghĩ rằng tính tham chiếu giao diện phải được tham gia. Có vẻ như đây không phải là trường hợp. – jpfollenius

+0

và +1 để trích xuất bản chất của vấn đề này. – jpfollenius

3

Trông tôi giống như một vấn đề tham chiếu vòng tròn xác định. Các phương thức ẩn danh được quản lý thông qua các giao diện ẩn và nếu TList<TListenerProc> được sở hữu bởi đối tượng ISomeInterface được triển khai, thì bạn có một vấn đề tham chiếu vòng tròn.

Một giải pháp có thể là đặt phương thức ClearListeners trên ISomeInterface gọi .Clear trên TList<TListenerProc>. Miễn là không có gì khác đang giữ một tham chiếu đến các phương thức nặc danh, điều đó sẽ làm cho tất cả chúng biến mất và thả các tham chiếu của chúng tới ISomeInterface.

Tôi đã thực hiện một số bài viết về cấu trúc và triển khai các phương pháp ẩn danh có thể giúp bạn hiểu những gì bạn đang thực sự làm việc và cách chúng hoạt động tốt hơn một chút. Bạn có thể tìm thấy chúng tại http://tech.turbu-rpg.com/category/delphi/anonymous-methods.

+0

Và nơi để gọi phương thức 'ClearListeners'? – jpfollenius

+0

Xin lỗi nếu đây là câu trả lời hơi chung chung, nhưng "trong khi dọn dẹp". Bất cứ khi nào bạn muốn tất cả điều này đi ra khỏi phạm vi. –

+0

Cảm ơn cho đến nay, Mason! Bạn có thể xem câu hỏi đã chỉnh sửa của tôi và mã ví dụ không? Khi tôi đặt cuộc gọi đến 'ClearListeners' ở cuối phương thức chính, phương thức ẩn danh vẫn bị rò rỉ. – jpfollenius

1

Vấn đề là với các phương thức ẩn danh trong chính dpr.

Chỉ cần đặt mã của bạn trong một thói quen và gọi rằng trong chính dpr và báo cáo rò rỉ bộ nhớ đã biến mất.

procedure Main; 
var 
    Obj: ISomeInterface; 
begin 
    try 
    ReportMemoryLeaksOnShutdown := True; 
    Obj := TSomeInterface.Create; 
    Obj.AddListener (MyListener); 
    Obj.NotifyListeners; 
    Obj := nil; 
    except 
    on E: Exception do 
     Writeln(E.ClassName, ': ', E.Message); 
    end; 
end; 

begin 
    Main; 
end.