CnPack Forum » 技术板块灌水区 » 不得不说的delphi技巧 之二-----------智能指针


2007-2-1 21:13 zzzl
不得不说的delphi技巧 之二-----------智能指针

先直接看用法

procedure p();
var
   t: TT;
begin
   t:=TT.create;
   TSafeObject.safeObject(t); //用智能指针保护t对象
   //现在,t对象无需手工free,不管过程内发生什么事,它在过程退出后都会自动释放
end;

下面是TSafeObject的定义

TSafeObject=class(TInterfacedObject)
private
   obj: TObject;
   destructor Destroy(); override;
   constructor create(obj: TObject); overload;
public
   class function safeObject(instance: TObject): IInterface;     
end;

class function TSafeObject.safeObject(instance: TObject): IInterface;
begin
   result:=TSafeObject.create(instance);
end;

2007-2-1 21:20 zzzl
constructor TSafeObject.create(obj: TObject);
begin
self.obj:=obj;
end;

destructor TSafeObject.Destroy;
begin
obj.Free;
inherited;
end;

2007-2-2 09:30 Passion
阐述一下原理?

2007-2-2 09:34 jAmEs_
晕。。。刚发错地方了
这个有用~~

2007-2-2 10:17 shenloqi
我也写了一个这样的东西,先是代码:
[code]unit uScopeGuard;
interface
uses
  SysUtils, Classes, Contnrs;
type
  TShenMethod = record
    Code, Sender, Data: Pointer;
    T: Integer;
  end;
  TShenMethodDynArray = array of TShenMethod;
  TObjProcedure = procedure (Sender: TObject);
  TObjectGuard = class;
  IScopeGuard = interface
  ['{7561A2E9-F368-429F-9982-BCFFACC32573}']
    function CreateObject(AObject: TObject): TObject;
    function CreateSubScopeGuard: IScopeGuard;
    procedure Add(AObject: TObjectGuard);
    procedure Clear;
    procedure DismissAll;
    procedure Remove(AObject: TObjectGuard);
  end;
  TScopeGuard = class(TInterfacedObject, IScopeGuard)
  private
    FStack: TStack;
  protected
    procedure Clear; virtual;
  public
    constructor Create;
    destructor Destroy; override;
    function CreateObject(AObject: TObject): TObject;
    function CreateSubScopeGuard: IScopeGuard;
    procedure Add(AObject: TObjectGuard);
    procedure DismissAll;
    procedure Remove(AObject: TObjectGuard);
  end;
  TObjectGuard = class
  private
    FDismiss: Boolean;
  protected
    FMethods: TShenMethodDynArray;
    constructor InternalCreate;
    procedure AddMethods(M: TShenMethodDynArray); virtual;
  public
    constructor Create;
    destructor Destroy; override;
    procedure CleanUp; virtual;
    procedure Dismiss;
  end;
  TObjectGuardFactory = class
  public
    class function Make(M: TShenMethod): TObjectGuard; overload;
    class function Make(M: array of TShenMethod): TObjectGuard; overload;
  end;
  ISG = IScopeGuard;
  TSG = TScopeGuard;
  TOG = TObjectGuard;
  TOGF = TObjectGuardFactory;
function MakeMethod(const Code: PLongint; const Sender: Pointer = nil; const Data: Pointer = nil): TShenMethod; overload;
function MakeMethod(const Code: TProcedure): TShenMethod; overload;
function MakeMethod(const Code: TObjProcedure; const Sender: TObject): TShenMethod; overload;
function MakeMethod(const Code: TNotifyEvent; const Sender, Data: TObject): TShenMethod; overload;
function MakeMethod(const Code: TThreadMethod; const Sender: TObject): TShenMethod; overload;
function MakeFreeAndNil(var P): TShenMethod;
function MakeAssignOrd(var Dest; const Src: Integer): TShenMethod;
function MakeAssignStr(var Dest: string; const Src: string): TShenMethod;
procedure CallMethod(const M: TShenMethod); overload;
implementation
const
  SMNormal = 0;
  SMFreeAndNil = 1;
  SMAssignOrd = 2;
  SMAssignStr = 3;
function MakeMethod(const Code: PLongint; const Sender, Data: Pointer): TShenMethod;
begin
  Result.Code := Code;
  Result.Sender := Sender;
  Result.Data := Data;
  Result.T := SMNormal;
end;
function MakeMethod(const Code: TProcedure): TShenMethod;
begin
  Result := MakeMethod(PLongint(@Code));
end;
function MakeMethod(const Code: TObjProcedure; const Sender: TObject): TShenMethod;
begin
  Result := MakeMethod(PLongint(@Code), Sender);
end;
function MakeMethod(const Code: TNotifyEvent; const Sender, Data: TObject): TShenMethod;
begin
  Result := MakeMethod(PLongint(@Code), Sender, Data);
end;
function MakeMethod(const Code: TThreadMethod; const Sender: TObject): TShenMethod;
begin
  Result := MakeMethod(PLongint(@Code), Sender);
end;
function MakeFreeAndNil(var P): TShenMethod;
begin
  Result.Code := nil;
  Result.Sender := nil;
  Result.Data := @P;
  Result.T := SMFreeAndNil;
end;
function MakeAssignOrd(var Dest; const Src: Integer): TShenMethod;
begin
  Result.Code := nil;
  Result.Sender := Pointer(Src);
  Result.Data := @Dest;
  Result.T := SMAssignOrd;
end;
function MakeAssignStr(var Dest: string; const Src: string): TShenMethod;
begin
  Result.Code := nil;
  Result.Sender := PChar(Src);
  Result.Data := PChar(@Dest);
  Result.T := SMAssignStr;
end;
procedure CallMethodNormal(const M: TShenMethod);
asm
  CMP  M.Code, 0;
  JZ   @@end;
  MOV  ECX, EAX;
  MOV  EAX, [ECX].TShenMethod.Sender;
  MOV  EDX, [ECX].TShenMethod.Data;
  Call [ECX].TShenMethod.Code;
@@end:
end;
procedure CallMethodFreeAndNil(const M: TShenMethod);
begin
  FreeAndNil((M.Data)^);
end;
procedure CallMethodAssignOrd(const M: TShenMethod);
begin
  PLongint(M.Data)^ := Longint(M.Sender);
end;
procedure CallMethodAssignStr(const M: TShenMethod);
var
  s: string;
begin
  s := StrPas(M.Sender);
  PChar((M.Data)^) := PChar(s);
end;
procedure CallMethod(const M: TShenMethod);
begin
  case M.T of
    SMNormal: CallMethodNormal(M);
    SMFreeAndNil: CallMethodFreeAndNil(M);
    SMAssignOrd: CallMethodAssignOrd(M);
    SMAssignStr: CallMethodAssignStr(M);
  end;
end;
{ TScopeGuard }
type TStackAccess = class(TStack);
procedure TScopeGuard.Add(AObject: TObjectGuard);
begin
  FStack.Push(AObject);
end;
procedure TScopeGuard.Clear;
var
  AObject: TObject;
begin
  while FStack.Count > 0 do
  begin
    AObject := FStack.Pop;
    if Assigned(AObject) then
    begin
      AObject.Free;
    end;
  end;
end;
constructor TScopeGuard.Create;
begin
  inherited Create;
  FStack := TStack.Create;
end;
function TScopeGuard.CreateObject(AObject: TObject): TObject;
begin
  Result := AObject;
  Add(TObjectGuardFactory.Make(MakeMethod(Result.Free, Result)));
end;
function TScopeGuard.CreateSubScopeGuard: IScopeGuard;
begin
  Result := TScopeGuard.Create;
end;
destructor TScopeGuard.Destroy;
begin
  Clear;
  FStack.Free;
  inherited;
end;
procedure TScopeGuard.DismissAll;
var
  i: Integer;
  AObject: TObjectGuard;
begin
  with TStackAccess(FStack).List do
  begin
    for i := Count - 1 downto 0 do
    begin
      AObject := Items[i];
      AObject.Dismiss;
    end;
  end;
end;
procedure TScopeGuard.Remove(AObject: TObjectGuard);
begin
  TStackAccess(FStack).List.Remove(AObject); // Do not free AObject
end;
{ TObjectGuard }
procedure TObjectGuard.AddMethods(M: TShenMethodDynArray);
var
  i, OldLen, MLen: Integer;
begin
  MLen := Length(M);
  if MLen > 0 then
  begin
    OldLen := Length(FMethods);
    SetLength(FMethods, OldLen + MLen);
    for i := 0 to MLen - 1 do
    begin
      FMethods[OldLen + i] := M[i];
    end;
  end;
end;
procedure TObjectGuard.CleanUp;
var
  i: Integer;
begin
  for i := 0 to Length(FMethods) - 1 do
  begin
    CallMethod(FMethods[i]);
  end;
end;
constructor TObjectGuard.Create;
begin
  raise Exception.Create('Please use TObjectGuardFactory.make instead of TObjectGuard.Create');
end;
destructor TObjectGuard.Destroy;
begin
  if not FDismiss then CleanUp;
  inherited;
end;
procedure TObjectGuard.Dismiss;
begin
  FDismiss := True;
end;
constructor TObjectGuard.InternalCreate;
begin
  inherited Create;
end;
{ TObjectGuardFactory }
class function TObjectGuardFactory.Make(M: TShenMethod): TObjectGuard;
var
  _M: TShenMethodDynArray;
begin
  Result := TObjectGuard.InternalCreate;
  SetLength(_M, 1);
  _M[0] := M;
  Result.AddMethods(_M);
end;
class function TObjectGuardFactory.Make(M: array of TShenMethod): TObjectGuard;
var
  _M: TShenMethodDynArray;
  I, Count: Integer;
begin
  Result := nil;
  Count := Length(M);
  if Count <= 0 then Exit;
  Result := TObjectGuard.InternalCreate;
  SetLength(_M, Count);
  for I := 0 to Count - 1 do _M[I] := M[I];
  Result.AddMethods(_M);
end;
end.[/code]

2007-2-2 10:21 shenloqi
然后是用法
[code]type
  TMO = class(TObject)
    procedure DoIt;
    procedure Undo;
  end;
  TDO = class(TObject)
    procedure DoIt;
    procedure Undo;
  end;
procedure TMO.DoIt;
begin
  ShowMessage('Do memory operation');
end;
procedure TMO.Undo;
begin
  ShowMessage('Undo memory operation');
end;
procedure TDO.DoIt;
begin
  raise Exception.Create('Exception while processing database operation');
  ShowMessage('Do database operation');
end;
procedure TDO.Undo;
begin
  ShowMessage('Undo database operation');
end;
procedure TForm3.NotUseGuardClick(Sender: TObject);
var
  _MO, _MO2: TMO;
  _DO: TDO;
begin
  _MO := TMO.Create;
  try
    _MO.DoIt;
    _MO2 := TMO.Create;
    try
      _MO2.DoIt;
      _DO := TDO.Create;
      try
        try
          _DO.DoIt;
        except
          on E: Exception do
          begin
            _MO2.Undo;
            _MO.Undo;
            raise ;
          end;
        end;
      finally
        _DO.Free;
      end;
    finally
      _MO2.Free;
    end;
  finally
    _MO.Free;
  end;
end;
procedure TForm3.UseGuardClickClick(Sender: TObject);
var
  SG, SGUndo: ISG;
  _MO, _MO2: TMO;
  _DO: TDO;
begin
  SG := TScopeGuard.Create;
  SGUndo := TScopeGuard.Create;
  _MO := TMO(SG.CreateObject(TMO.Create));
  _MO.DoIt;
  SGUndo.Add(TOGF.Make(MakeMethod(_MO.Undo, _MO)));
  _MO2 := TMO(SG.CreateObject(TMO.Create));
  _MO2.DoIt;
  SGUndo.Add(TOGF.Make(MakeMethod(_MO2.Undo, _MO2)));
  _DO := TDO.Create;
  SG.Add(TOGF.Make(MakeMethod(_DO.Free, _DO)));
  _DO.DoIt;
  SGUndo.DismissAll; // or SGUndo.Clear;
end;[/code]

可以比较NotUseGuardClick和UseGuardClick,它们做的事情是一样的,不过后者会少些代码和更安全

[[i] 本帖最后由 shenloqi 于 2007-2-2 10:27 编辑 [/i]]

2007-2-2 10:25 shenloqi
我写的这个不是很方便使用,因为功能不只是保证释放应该释放的对象,还包括的Undo等处理;代码也只是自己以前实验的代码,后来也就没有再管了

2007-2-2 10:31 crystal999
这个很明显盗用了接口得自动管理功能

2007-2-2 10:33 shenloqi
还有一些演示的方法:
[code]
procedure TForm3.Button12Click(Sender: TObject);
var
  SG: ISG;
  ss: TStringList;
begin
  SG := TSG.Create;
  ss := TStringList.Create;
  SG.Add(TOGF.Make(MakeMethod(ss.Free, ss)));
  with SG.CreateSubScopeGuard do
  begin
    ss := TStringList.Create;
    Add(TOGF.Make(MakeFreeAndNil(ss)));
  end;
end;[/code]

[code]procedure TForm3.Button8Click(Sender: TObject);
var
  SG: ISG;
  OG: TOG;
  TestException: TTestException;
  i: Integer;
begin
  SG := TSG.Create;
  for i := 0 to 9999 do
    try
      TestException := TTestException.Create;
      OG := TOGF.Make(MakeMethod(TestException.Close, TestException));
      SG.Add(OG);
      if i mod 2 = 0 then
        TestException.RaiseException;
      TestException.Close;
      OG.Dismiss;
    except
    end;
end;[/code]

[code]procedure TForm3.Button14Click(Sender: TObject);
var
  SG: ISG;
  ss, ss1: TStringList;
begin
  SG := TSG.Create;
  ss := TStringList.Create;
  SG.Add(TOGF.Make(MakeFreeAndNil(ss)));
  with SG.CreateSubScopeGuard do
  begin
    ss1 := TStringList.Create;
    Add(TOGF.Make(MakeFreeAndNil(ss1)));
    ss1.Free;
    DismissAll;
  end;
  ss.Free;
  SG.DismissAll;
end;[/code]

2007-2-2 14:38 kendling
之前也看过类似的代码(好像是说什么类似JAVA的自动回收机制),不错,

页: [1]


Powered by Discuz! Archiver 5.0.0  © 2001-2006 Comsenz Inc.