一般情况下对象类型作为参数传递时,传递的类型为引用型。因此,当对传入的参数执行Free时,该对象将被从内存清除。那么所有对它的引用将失效
但最近在写TComboBox相关的赋值操作时却发现,TComboBox的Items属性为TStrings类型,但执行
combobox.items := stringlist;
之后free掉stringlist,发现TComboBox的Items并没有被free掉,而是保持了原来的数据。
因此,我们可以想到在TComboBox的Items赋值操作中有一个数据复制机制,而不是简单的将引用赋值给Items属性。
打开stdCtrls单元,找到TCustomCombo的Items定义位置(TComboBox继承自TCustomCombo,其Items属性在TCustomCombo实现),我们发现下面一条语句:
property Items: TStrings read FItems write SetItems;
嗯,果然有一个SetItems,我们一起来看一下这个函数的实现,真相大白:
procedure TCustomCombo.SetItems(const Value: TStrings);
begin
if Assigned(FItems) then
FItems.Assign(Value)
else
FItems := Value;
end;
SetItems函数使用了其父类TPersistent的Assign函数来实现数据赋值,而只有在FItems成员为nil时才会复制引用,那么TComboBox必定在创建之时创建了FItems成员
为了验证这点,因为TComboBox仅仅是将TCustomComboBox封装成标准的VCL控件,本身并没有代码实现,因此,我们找到了TComboBox的直接父类TCustomComboBox,在它的Create函数中有这么一句:
FItems := GetItemsClass.Create;
这里VCL使用了一个技巧(此技巧在VCL Framework中大量出现),用GetItemsClass返回类对象,然后用其创建对象。
由此我们可以看到,FItems确实是在TComboBox创建的时候被初始化了,因此,只要没有去显式的free它,Items属性的赋值操作均为数据复制。
到此也找到了上述看似奇怪的问题。
VCL Framework中使用了大量的技巧以保证其高度的灵活性和极高的执行效率,多多深入研究VCL的实现方法对我们的设计思想大有帮助。