Delphi 字符串的引用计数与生命周期

先来段代码

 

type
  MyString = AnsiString;
  PMyChar = PAnsiChar;


procedure TForm2.Button2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption := 'frmTest';  //7位的字符串
  p := GetCaption;
  s2 := p;   //这是时候s2 为frmTes
  ShowMessage(s2);  //*****显示出来为frmTes
end;

function TForm2.GetCaption: PMyChar;
var
  s1, s2: MyString;
begin
  s2 := MyString(self.Caption);
  Result := PMyChar(MyString(s2));
end;

 

研究说明(代表个人意见)(XE下面测试)

 

function TForm2.GetCaption: PMyChar;
var
  s1: MyString;
begin
  s1 := MyString(Self.Caption);    //self.Caption源码得知,是获取了一块临时的空间(A1)
  //A1(integer(s1))

  i:= StringRefCount(s1);  //i=1

  Result := PMyChar(MyString(s1));  //Result指针指向的为(A1)的空间
  //Integer(@Result^) = A1(integer(s1))  是指向同一块空间

  i:= StringRefCount(s1);  //i=1
end;
//函数返回后s1因为是局部变量 s1的引用计数为0,integer(s1)的空间被标志为可以覆盖
//返回的为指针,不增加s1的引用计数


procedure TForm2.Button2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption := 'frmTest';
  p := GetCaption; //实际上p指向的那块地址被标注为可以覆盖,随时都有可能被覆盖,是很危险的
  //Integer(@p^) = GetCaption内部给s1分配的那块空间地址
 
  s2 := p;         //导致丢掉了字符..
  //因为p指向的内存是可以被覆盖的,s2分配的地址可能和p指向的地址是一样的,导致丢掉了字符..
  //Integer(s2) 可能= Integer(@p^)  测试是发现都一样

  //下面操作(SetLength)同样也会一样结果,s2占用的和p占用的同样大小(或者小)。
  //这样导致了s2分配的空间可能和p内存一样
  //如果7改成较大的数就正常
  //  SetLength(s2, 7);
  //  StrCopy(PMyChar(s2), p);

  ShowMessage(s2);  //错误
end;

 

 解决方案(1)

将s1定义为内成员变量,这样当GetCaption执行完后那块空间不会被标准为可读写

 

function TForm2.GetCaption2: PMyChar;
begin
  FMyCaption := MyString(Self.Caption);    //self.Caption源码得知,是获取了一块临时的空间(A1)
  //A1(integer(FMyCaption))

  i:= StringRefCount(FMyCaption);  //i=1

  Result := PMyChar(MyString(FMyCaption));  //Result指针指向的为(A1)的空间
  //Integer(@Result^) = A1(integer(FMyCaption))  是指向同一块空间

  i:= StringRefCount(FMyCaption);  //i=1
end;
//执行完后FMyCaption不是临时变量,指向的地址不可以被覆盖

procedure TForm2.btnGetCaption2Click(Sender: TObject);
var
  p: PMyChar;
  s, s2: MyString;
begin
  self.Caption := 'frmTest';
  p := GetCaption2; //实际上p指向的那块地址和FMyCaption的地址是一样的
  //Integer(@p^) = GetCaption2内部给FMyCaption分配的那块空间地址是一样

  s2 := p;
  //因为p指向的内存是不可以被覆盖的,s2分配的地址不可可能和p指向的地址是一样的,这样做是安全的
  //Integer(s2) <> Integer(@p^)

  i:= StringRefCount(s2);  //i=1  新的内存

  ShowMessage(s2);  //正确
end;

 

 

***

局部变量

 

function TForm2.GetCaption2: PMyChar;
var

  s1: MyString;
begin
  s1 := 'frmTest'
  i:= StringRefCount(s1);  //i=-1  常量地址指向空间不可被覆盖
 
  //UniqueString(s1); 
  //i:= StringRefCount(s1);  //i=1  s1又变成临时的,函数返回后s1指向的地址不再安全
 
  Result=PMyChar(s1)
end;

 

 

 

posted @ 2011-10-20 13:54  D10.天地弦  阅读(557)  评论(0编辑  收藏  举报