2012-10-30 27 views
6

Sau khi đọc nhiều bài đăng trên StackOverflow về khuyết điểm của việc sử dụng tính toán tham chiếu tự động cho Giao diện, tôi bắt đầu cố gắng tham khảo thủ công đếm từng giao diện tức thời.Giao diện mà không cần tính tham chiếu

Sau khi thử một buổi chiều đầy đủ, tôi bỏ cuộc!

Tại sao tôi bị truy cập Vi phạm khi tôi gọi FreeAndNil (p)?

Dưới đây là danh sách đầy đủ đơn vị đơn giản của tôi.

unit fMainForm; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, StdCtrls; 

type 
    TForm4 = class(TForm) 
    btn1: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure btn1Click(Sender: TObject); 
    end; 

type 
    IPersona = interface(IInterface) 
    ['{44483AA7-2A22-41E6-BA98-F3380184ACD7}'] 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    property Nome: string read GetNome write SetNome; 
    end; 

type 
    TPersona = class(TObject, IPersona) 
    strict private 
    FNome: string; 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    protected 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; 
    public 
    constructor Create(const ANome: string); 
    destructor Destroy; override; 
    end; 

var 
    Form4: TForm4; 

implementation 

{$R *.dfm} 

procedure TForm4.FormCreate(Sender: TObject); 
begin 
    ReportMemoryLeaksOnShutdown := True; 
end; 

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    ShowMessage(p.Nome); 
    finally 
    FreeAndNil(p); 
    end; 
end; 

constructor TPersona.Create(const ANome: string); 
begin 
    inherited Create; 
    FNome := ANome; 
end; 

destructor TPersona.Destroy; 
begin 
    inherited Destroy; 
end; 

function TPersona._AddRef: Integer; 
begin 
    Result := -1 
end; 

function TPersona._Release: Integer; 
begin 
    Result := -1 
end; 

function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if GetInterface(IID, Obj) then 
    Result := S_OK 
    else 
    Result := E_NOINTERFACE; 
end; 

function TPersona.GetNome: string; 
begin 
    Result := FNome; 
end; 

procedure TPersona.SetNome(const Value: string); 
begin 
    FNome := Value; 
end; 

end. 

Trả lời

11

Vi phạm truy cập xảy ra vì FreeAndNil nhận được tham số var không được lấy mẫu được mong đợi là tham chiếu đối tượng. Bạn đang chuyển một tham chiếu giao diện không đáp ứng yêu cầu. Thật không may, bạn chỉ tìm ra lúc chạy. Điều này, theo quan điểm của tôi, điểm mạnh nhất chống lại việc sử dụng FreeAndNil.

Tính tham chiếu của bạn sẽ vô hiệu hóa quản lý lâu dài bằng cơ chế tính tham chiếu giao diện. Để phá hủy một đối tượng bạn cần gọi là destructor của nó. Và để làm điều đó bạn phải có quyền truy cập vào destructor. Giao diện của bạn không lộ ra destructor (và nó không nên). Vì vậy, chúng ta có thể suy luận rằng, để phá hủy đối tượng, bạn cần phải có một tham chiếu đối tượng.

Dưới đây là một số tùy chọn:

var 
    obj: TPersona; 
    intf: IPersona; 
.... 
obj := TPersona.Create('Fabio'); 
try 
    intf := obj; 
    //do stuff with intf 
finally 
    obj.Free; 
    // or FreeAndNil(obj) if you prefer 
end; 

Hoặc bạn có thể làm điều đó như thế này

var 
    intf: IPersona; 
.... 
intf := TPersona.Create('Fabio'); 
try 
    //do stuff with intf 
finally 
    (intf as TObject).Free; 
end; 
+0

+1 tôi không biết bạn chỉ có thể định kiểu một giao diện để một TObject. * (Chưa thử nghiệm nó, nhưng đến từ bạn, tôi giả sử nó là chính xác) * –

+2

@Lieven Chỉ trong một số phiên bản Delphi gần đây hơn được hỗ trợ. Hoặc là năm 2009 hoặc 2010. Không thể nhớ chính xác khi nào. –

+0

David cảm ơn bạn! giải pháp của bạn (cả hai) làm việc như một say mê (như đối với mỗi câu trả lời bạn đăng trên StackOverflow ;-)) GREAT !!!! –

0

Bạn không thể sử dụng FreeAndNil() với một tham chiếu giao diện, chỉ là một tài liệu tham khảo objct. Có bạn lại kích hoạt tính tham khảo của giao diện, bạn chỉ đơn giản là sẽ gán nil để tham chiếu giao diện (hoặc chỉ để cho nó đi ra khỏi phạm vi) để giải phóng các đối tượng một cách chính xác, ví dụ:

type 
    TPersona = class(TInterfacedObject, IPersona) 
    strict private 
    FNome: string; 
    function GetNome: string; 
    procedure SetNome(const Value: string); 
    public 
    constructor Create(const ANome: string); 
    destructor Destroy; override; 
    end; 

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    ShowMessage(p.Nome); 
    finally 
    p := nil; 
    end; 
end; 

Nhưng kể từ khi bạn đã vô hiệu hóa các tài liệu tham khảo đếm trên giao diện, bạn cần phải quay trở lại sử dụng các biến tham chiếu đối tượng bình thường trong mã của bạn, ví dụ:

procedure TForm4.btn1Click(Sender: TObject); 
var 
    p: TPersona; 
    intf: IPersona; 
begin 
    p := TPersona.Create('Fabio'); 
    try 
    if Supports(p, IPersona, intf) then 
     ShowMessage(intf.Nome); 
    finally 
    FreeAndNil(p); 
    end; 
end; 
Các vấn đề liên quan