所以我的程序中有一个测试用例
MemCheck<String>('a', 'a');
此测试用例失败,我不确定原因
c := a ;
使用
时效果很好 c := T(vals[2]);
代替UISearchController
答案 0 :(得分:16)
您正在将字符串文字传递给MemCheck()
。由于它们是相同的值,因此编译器将它们合并在可执行文件中。因此,您实际上将一个常量字符串文字传递给x
和y
参数。字符串文字是只读的,引用计数为-1。将字符串文字分配给String
变量将指针按原样复制到内存块,而不分配新内存或增加内存块的引用计数。
在填充vals
数组之前,您的a
,b
和c
变量只是字符串文字指针的副本。它们都指向内存中的相同数据块。 System.StringRefCount()
函数确认它们都返回引用计数-1,证明它们都指向字符串文字。
在语句vals[2] := a;
中,a
指向字符串文字,因此RTL分配新的String
实例,并将字符串文字的内容复制到新的内存块中。 a
仍然具有-1的引用计数,但vals[2]
现在的引用计数为1,证明已执行分配。然后,语句c := T(vals[2]);
将分配的String
分配给String
变量,因此分配的数据的引用计数会递增。 StringRefCount()
确认c
现在的引用计数为2.
CompareMem()
失败,因为你正在比较指向两个不同内存块的两个String
变量的内部数据指针的值,因此它们的指针值不同,比较失败。
当您将语句c := T(vals[2]);
更改为c := a;
时,a
仍然指向内存中的字符串文字,因此指针会按原样复制到c
中执行任何分配。因此CompareMem()
成功,因为现在您正在比较指向同一内存块的两个String
变量的内部数据指针的值,因此比较成功。
所以真正的问题是 - 为什么语句vals[2] := a;
执行新的分配而不是仅仅复制数据指针就像任何其他String := String;
赋值一样?
分配a := x;
,b := y;
和c := a;
正在调用System.@UStrLAsg()
函数,该函数允许指向字符串文字的String
指定为 - 是在没有执行分配的情况下转到另一个String
。
语句vals[2] := a;
正在调用System.@UStrAsg()
函数,当源String
指向字符串文字时,总是创建一个新的已分配副本。
为什么编译器选择在@UStrAsg()
语句中使用@UStrLAsg()
而不是vals[2] := a;
?这就是当赋值的左侧是动态数组中的String
而不是独立变量时编译器的工作方式。如果vals
是静态数组(vals: array[0..3] of String;
),则编译器会选择使用@UStrLAsg()
而不是@UStrAsg()
,然后CompareMem()
会成功。
@UStrLAsg()
通常在将String
分配给本地 String
时使用,因此通常可以安全地使用字符串文字作为 - 是
@UStrAsg()
通常在将String
分配给全局 String
时使用,其中需要复制文字以避免文字时出现潜在的内存错误可能存在于可在分配后卸载的DLL /包中。
因此,在分配给动态数组时,编译器必须小心谨慎,因为它不知道或不能假设数组中String
个实例的生命周期。