Delphi 无法释放IXMLDocument、IXMLNodeList或其他(omnixml';s)接口类型对象
下面的函数接收XML输入,对其进行解析,并返回一个普通字符串,该字符串仅显示在调用方函数中。因此,上下文中的对象是下面函数的内部对象 但是这个函数有一个奇怪的问题,它会记住输入,这意味着对象没有被正确释放。即使输入被检查为不同,输出字符串也有前一次调用产生的部分 在将XMLDoc、IXMLNodeList中的每一个都分配给nil之前,我还尝试在一个循环中通过将nil分配给它来释放直接从Item数组引用的每个IXMLNode,但该分配语句导致了一个语法错误,因此解决了以下问题Delphi 无法释放IXMLDocument、IXMLNodeList或其他(omnixml';s)接口类型对象,delphi,delphi-xe5,Delphi,Delphi Xe5,下面的函数接收XML输入,对其进行解析,并返回一个普通字符串,该字符串仅显示在调用方函数中。因此,上下文中的对象是下面函数的内部对象 但是这个函数有一个奇怪的问题,它会记住输入,这意味着对象没有被正确释放。即使输入被检查为不同,输出字符串也有前一次调用产生的部分 在将XMLDoc、IXMLNodeList中的每一个都分配给nil之前,我还尝试在一个循环中通过将nil分配给它来释放直接从Item数组引用的每个IXMLNode,但该分配语句导致了一个语法错误,因此解决了以下问题 function t
function tform1.nodeToSentence(nodeXml: string): string ;
var
tempXmlDoc : IXMLDocument;
resultWordPuncNodes : IXMLNodeList;
i: Integer;
begin
tempXmlDoc := CreateXMLDoc;
tempXmlDoc.LoadXML(nodeXml);
resultWordPuncNodes := XPathSelect(tempXmlDoc.DocumentElement,'//*');
for i:= 0 to resultWordPuncNodes.Length-1 do
begin
if resultWordPuncNodes.Item[i].NodeName = 'name' then
begin
if resultWordPuncNodes.Item[i].Attributes['attrib'] = 'v_attrib' then
begin
Result := TrimRight(Result);
Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end
else Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end;
end;
resultWordPuncNodes:=nil;
tempXmlDoc := nil; //interface based objects are freed this way
end;
呼叫码
//iterating over i
nodeXML := mugList.Strings[i];
readableSentence := nodeToSentence(mugList.Strings[i]);
//examplesList.Append(readableSentence);
//for debugging
showMessage(readableSentence);
//iteration ends
它与接口无关。 它在Delphi中只有字符串实现 必须清除结果变量作为函数的第一行 以下是已修复的函数:
function tform1.nodeToSentence(nodeXml: string): string ;
var
tempXmlDoc : IXMLDocument;
resultWordPuncNodes : IXMLNodeList;
i: Integer;
begin
// Change #1 - added the line
Result := '';
// Variable Result is shared here before by both the function and the caller.
// You DO HAVE to clear the shared variable to make the function FORGET the previous result.
// You may do it by the 'function' or by the calling site, but it should have be done.
// Usually it is better to do it inside the function.
tempXmlDoc := CreateXMLDoc;
tempXmlDoc.LoadXML(nodeXml);
resultWordPuncNodes := XPathSelect(tempXmlDoc.DocumentElement,'//*');
for i:= 0 to resultWordPuncNodes.Length-1 do
begin
if resultWordPuncNodes.Item[i].NodeName = 'name' then
begin
if resultWordPuncNodes.Item[i].Attributes['attrib'] = 'v_attrib' then
begin
/// REMEMBER this line, it is important, I would explain later.
Result := TrimRight(Result);
Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end
else Result := Result + resultWordPuncNodes.Item[i].Text + ' ';
end;
end;
resultWordPuncNodes:=nil;
// Change #2 - removed the line - it is redundant
(* tempXmlDoc := nil; //interface based objects are freed this way *)
// Yes, they are. But Delphi automatically clears local ARC-variables
// when destroying them where the function exits.
// Variable should both be local and should be one of ARC types foe that.
end;
1) 关于自动清除局部变量和圆弧类变量,请参见
2) 你真正的函数根本不是一个函数
// function tform1.nodeToSentence(nodeXml: string): string ; // only an illusion
procedure tform1.nodeToSentence(nodeXml: string; var Result: string); // real thing
你可以说你在写一个函数,而不是过程。
然而,这仅仅是一种句法上的糖分。
就像TDateTime
和double
是不同的术语,但这些术语是相同实现的同义词
Delphi中所有返回String/AnsiString/UnicodeString变量的函数都是过程。它们只是伪装成功能,伪装很薄
3) 关于这件事,有一个古老的德尔菲·卡恩(Delphi kōan)。没有OmniXML和所有只会模糊真相的复杂内容。运行此代码并查看其行为
Function Impossible: String;
begin
ShowMessage( 'Empty string is equal to: ' + Result );
end;
Procedure ShowMe; Var spell: string;
begin
spell := Impossible();
spell := 'ABCDE';
spell := Impossible();
spell := '12345';
spell := Impossible();
end;
4) 现在,你能知道吗?是的,你可以,它只需要一点点的关注。让我们做一点改变
Function ImpossibleS: String;
begin
ShowMessage( 'Unknown string today is equal to: ' + Result );
end;
Function ImpossibleI: Integer;
begin
ShowMessage( 'Unknown integer today is equal to: ' + IntToStr(Result) );
end;
Procedure ShowMe;
Var spell: string; chant: integer;
begin
spell := ImpossibleS();
spell := 'ABCDE';
spell := ImpossibleS();
spell := '12345';
spell := ImpossibleS();
chant := ImpossibleI();
chant := 100;
chant := ImpossibleI();
chant := 54321;
chant := ImpossibleI();
end;
注意并阅读编译警告。您确实修复了它编译时没有编译器警告的原始代码,不是吗
现在编译我的第二个kōan。阅读警告。整数函数不会生成“未初始化变量”警告。字符串函数不支持。是这样吗
为什么不同?因为字符串是圆弧类型。这意味着字符串函数实际上是一个过程,而不是一个函数。这意味着结果变量由调用者在looks-like函数之外初始化。您还可以观察到chant
变量在调用后确实会发生变化,因为整数函数是实函数,而调用后的赋值是实函数。相反,字符串函数是虚幻的,调用后的虚幻赋值也是虚幻的。它看起来是被分配的,但不是
5) 最后一件事。为什么我要在你的密码里加上那个标记
/// REMEMBER this line, it is important, I would explain later.
Result := TrimRight(Result);
正是因为上面的那个kōan。你一定是在这里读垃圾。您一定使用了“Result”变量,而您以前在任何地方都没有初始化该变量。您一定期望编译器在这一行向您发出警告。就像上面的实函数一样。但事实并非如此。就像它不具有不可能的幻觉功能一样。这种警告的缺失是德尔福在这里创造的幻觉的一个致命的赠品。您是否注意到应该有一个“未初始化的变量”警告,但它丢失了?您会问自己“如果不是我的函数,是谁初始化了变量”,您自己就会明白发生了什么事。;-)
6) 好的,还有最后一件事
procedure StringVar( const S1: string; var S2: string );
begin
ShowMessage ( S1 + S2 );
end;
procedure StringOut( const S1: string; out S2: string );
begin
ShowMessage ( S1 + S2 );
end;
这两个过程看起来很相似。区别是两种。它应该是一个OUT参数,而不是VAR(IN+OUT)参数。但是Delphi在这里仅仅使用了您的函数就失败了。Delphi不能做字符串类型输出参数。要进行比较,FreePascal(Lazarus,CodeTyphon)知道差异,并将为StringOut
过程显示合法的“未初始化变量”警告
但是这个函数有一个奇怪的问题,它会记住输入,这意味着对象没有被正确释放。即使输入被检查为不同,输出字符串也有前一次调用产生的部分
这是因为函数
的字符串
返回值(以及其他ARC管理类型,以及记录
、对象
和方法指针)在调用者和被调用者之间通过使用隐藏的变量
参数传递,该参数在输入函数时不会自动清除,正如你所期待的
此代码:
function tform1.nodeToSentence(nodeXml: string): string ;
...
readableSentence := nodeToSentence(mugList.Strings[i]);
实际上与本规范相同:
procedure tform1.nodeToSentence(nodeXml: string; var Result: string);
...
nodeToSentence(mugList.Strings[i], readableSentence);
多次调用nodeToSession()
,可能会在同一字符串
变量中追加越来越多的文本
在函数内部,您需要手动重置结果
值,然后才能开始将新值连接到该值:
function tform1.nodeToSentence(nodeXml: string): string ;
var
...
begin
Result := ''; // <-- add this!
...
end;
函数tform1.nodeToSession(nodeXml:string):string;
变量
...
开始
结果:='';//我已经添加了调用部分。是的,您忘记清除readable句子
变量,即;如果我不关心被调用函数中传递的'Result'参数的清除,它不是在第一行被清除了吗(Result=nil)?您不能将nil赋值给字符串变量。是的,如果分配给它,它将被清除。但你没有分配给它。就这样。如果你想清除它,你必须清除它。无论是在函数内部还是外部,但您必须这样做。深入了解问题(字符串返回实际上是一个'var'参数)、垃圾源(Result:=TrimRight(Result))、关于XMLDoc释放的提示,以及所有基本内容(koan方法、弧类型、编译器警告等),这是一个很好的答案。这将对未来的读者非常有帮助。遗憾的是,我们越来越习惯于“永恒的九月”这类读者。那些抱着“只要修改代码,不要让我思考,这会伤害我”的态度的人。看到喜欢主动思考和解释的新读者,真是令人耳目一新