2011-12-21 25 views
7

Tôi nhận được vi phạm truy cập từ thiết bị DBXCommon.pas (trong Delphi XE). Khi tôi nhìn vào đoạn code tôi nhìn thấy những thứ như sau (tại dấu chấm than):Không nên gọi miễn phí trên một tham chiếu đối tượng được đặt thành nil để ném Vi phạm Truy cập mỗi khi được gọi?

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext; 
    const ConnectionProperties: TDBXProperties): TDBXConnection; 
var 
    ConnectionBuilder: TDBXConnectionBuilder; 
    DelegatePath:  TDBXDelegateItem; 
    Connection:   TDBXConnection; 
    CombinedProperties: TDBXProperties; 
begin 
    //... 
    ConnectionBuilder := TDBXConnectionBuilder.Create; 
    Connection  := nil; 
    try 
    //..lots of setting ConnectionBuilder properties 
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password]; 
    Connection := ConnectionBuilder.CreateConnection; 
    Connection.Open; 
    Result  := Connection; 
!! Connection := nil; 
    finally 
!! Connection.Free; 
    ConnectionBuilder.Free; 
    end; 
end; 

Nhưng tôi thấy cấu trúc như thế này (lần đầu tiên gán Nil, sau đó một miễn phí) nhiều hơn nữa trong DBXCommon.pas. Đây có phải là một số cấu trúc tôi không biết, hoặc là điều này thực sự gây ra vi phạm truy cập mỗi lần đoạn mã này được gọi là?

+1

Dupe - http://stackoverflow.com/questions/8548843/why-should-i-not-use-if-assigned-before-using-or-freeing-things – OnTheFly

+2

@user điều này dường như không phải là dupe. –

Trả lời

15

Gọi Free trên tham chiếu không phải lúc nào cũng an toàn. Hãy xem xét việc triển khai TObject.Free để xem lý do.

Mã này là ví dụ về chức năng nhà máy . Công việc của nó là tạo ra một thể hiện mới của một lớp, nhưng nếu nó không thành công, nó cần đảm bảo nó không làm rò rỉ một thể hiện được tạo ra một nửa khi nó ném một ngoại lệ, vì vậy nó gọi Free. Khi nó chắc chắn nó sẽ thành công, nó chuyển quyền sở hữu kết quả cho người gọi. Nó vẫn gọi Free, nhưng nếu nó đã được chuyển quyền sở hữu, sau đó nó kết thúc lên gọi Free trên một tham chiếu null, và không có hại. Mã này là những gì chuyển quyền sở hữu:

Result := Connection; 
Connection := nil; 

Cách tôi sẽ viết một hàm nhà máy sẽ loại bỏ các biến Connection riêng biệt. Tôi muốn xây dựng kết quả trực tiếp trong Result, nhưng giải phóng được nó nếu có một ngoại lệ, như thế này:

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext; 
    const ConnectionProperties: TDBXProperties): TDBXConnection; 
var 
    ConnectionBuilder: TDBXConnectionBuilder; 
    DelegatePath:  TDBXDelegateItem; 
    Connection:   TDBXConnection; 
    CombinedProperties: TDBXProperties; 
begin 
    //... 
    ConnectionBuilder := TDBXConnectionBuilder.Create; 
    try 
    //..lots of setting ConnectionBuilder properties 
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password]; 
    Result := ConnectionBuilder.CreateConnection; 
    try 
     Result.Open; 
    except 
     Result.Free; 
     raise; 
    end; 
    finally 
    ConnectionBuilder.Free; 
    end; 
end; 

đó có tác dụng tương tự.

+0

Cảm ơn, một nơi nào đó ở phía sau đầu tôi biết điều này. Tuy nhiên, viết nó như bạn thấy sẽ rõ ràng hơn cho mắt chưa được đào tạo. – Geerten

+2

+1 đây là cách thành ngữ để viết một hàm trả về một đối tượng mới –

+0

Tôi đồng ý với David, ngoại trừ việc nếu bạn không nâng lại ngoại lệ, bạn nên đặt Kết quả thành số không. Đó là điều chúng tôi làm trong mã hiệu năng quan trọng, nơi kiểm tra kết quả phi nil nhanh hơn nâng cao và xử lý ngoại lệ ... –

6

Gọi an toàn Free trên nil khi thực hiện kiểm tra cho Self <> nil trước khi gọi Destroy. Xem lời giải thích của Allen Bauer tại số Embarcadero forum lý do tại sao TObject.Free được giới thiệu. Tôi chỉ bao gồm các trích dẫn liên quan ở đây:

Lý do duy nhất để giới thiệu các phương pháp miễn phí không ảo trên TObject, là để sử dụng trong hàm hủy như một cách viết tắt đơn giản cho:

if FField <> nil then 
    FField.Destroy; 
+2

+1 cho bit của Delphi lịch sử ném trong :-) – talereader

+0

diễn đàn ở trên là không hợp lệ bây giờ. –

4

TObject.Free là về cơ bản được triển khai dưới dạng if Self <> nil then Destroy, vì vậy đoạn mã trên không nên tăng bất kỳ ngoại lệ nào.

Các vấn đề liên quan