这几天将D7下的一些组件转移到XE系列的编译环境中,根据网上别人写的总结文章进行改造,整个过程还算比较顺利,很快就可以编译通过并运行测试程序。但是随着测试工作的逐渐深入,一些以前没有注意到的问题慢慢浮上水面,并且花费了很长时间才最终找到问题的原因。
其中一个困扰我很久的问题就是,动态数组是怎么解释的?回忆了一下,在Delphi7中,我们通常是使用SetLength来为一个动态数组分配内存,比如下面这个例子:
type
TTestData = array of Byte;
function GetTestData(ASize: Integer): TTestData;
begin
SetLength(Result, ASize);
end;
当调用GetTestData后,我们就获得了一个大小为ASize的动态数组。我们可以将它保存到一个变量中,并且,可以对这个变量进行相应的操作:
var
FTestData: TTestData;
procedure TForm1.Button1Click(Sender: TObject);
begin
FTestData := GetTestData(100);
FTestData[50] := 10;
end;
那当不再需要使用时,我们需要如何释放这个数组呢?一般来说有两种做法,一种是直接将变量置为nil值,如:
FTestData := nil;
或者,是使用SetLength将数组长度设置为0:
SetLength(FTestData, 0);
这两种写法都可以实现将动态数组释放的功能。以前只知道使用,我一直没想过为什么这两句代码可以将动态数组释放掉的问题。不过一些基础的概念还是知道的,比如说知道动态数组的内存是由系统自动分配的,并且由系统自动回收,而这一功能得益于它的引用计数功能。我们上面调用
SetLength
实际上并不是一个内存分配函数,我们只是告诉
Delphi
,我们需要一个多大的数组,
Delphi
自动帮我们申请内存并返回。当我们尝试改动一个动态数组长度的时候,我们来看下这个过程中
Delphi
会帮我们做什么,比如:
FTestData := nil; //清空变量
SetLength(FTestData, 50); //第一次设置数组长度,此时Delphi直接申请一个足够存放50长度的数组空间内存。
SetLength(FTestData, 100);//第二次设置长度,这里需要扩展内存。如果当前内存空间充足,那么就直接在原址进行扩展。
//如果当前地址段内存空间不足,那么,Delphi会重新申请另一个地址空间,然后将原来数组里的数据搬移过去,最后,
//再将原来的内存释放掉。整个过程类似于内存重分配函数,ReallocMem
这个是我所理解的动态数组的管理方法,不知道是否正确,但我想原理应该差不多。我唯一不清楚的是,Delphi如何管理数组的引用计数?引用计数是什么时候被改变的?就因为这个问题没搞清楚,导致我前段时间调试一个程序时一直找不到出现BUG原因在哪,一度纠结了两天时间。于是我决定这次一定要弄清楚,这个引用计数到底是如何管理的。
我们先看一下动态数组在内存中的保存格式,该结构在D7跟XE下是一致的:
引用计数 数组长度 数组内容
&n