2016-12-07 9 views
1

Нижеприведенная ниже функция использует вход XML, анализирует его и возвращает обычную строку, которая просто отображается в функции вызывающего абонента. Таким образом, объекты в контексте являются внутренними для функции ниже.Невозможно освободить объекты IXMLDocument, IXMLNodeList или другие объекты типа объекта omnixml

Но эта функция имеет странную проблему, которая запоминает входные данные, что означает, что объекты не выпущены должным образом. Строка вывода имеет часть, полученную в результате предыдущего вызова, даже если вход отмечен как отличающийся.

Прежде чем назначить nil каждый из XMLDoc, IXMLNodeList, я также попытался выпустить в цикле каждый IXMLNode, непосредственно переданный из массива Item, назначив ему nil, но этот оператор присваивания привел к синтаксической ошибке, ниже.

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 
+0

Я добавил раздел призванию. – user30478

+0

Да, вы забыли очистить переменную 'readableSentence', что, это не о XML –

ответ

1

Это не нужно делать с интерфейсами. Это только с реализацией строки в Delphi.

Вы должны очистить переменную Result в качестве первой строки вашей функции.

Здесь ниже ваша функция фиксированной:

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) о авто-клиринговых местного и ARC-любезные переменных см http://docwiki.embarcadero.com/Libraries/XE8/en/System.Finalize

2) Ваша действительная функция не является функцией на всех.

// function tform1.nodeToSentence(nodeXml: string): string ; // only an illusion 
procedure tform1.nodeToSentence(nodeXml: string; var Result: string); // real thing 

Вы можете сказать, что писали функцию, а не процедуру. Однако это был просто синтаксический сахар. Это как TDateTime и double - это разные термины, но эти термины являются синонимами для одной и той же реализации;

Все функции, возвращающие переменные String/AnsiString/UnicodeString в Delphi, являются процедурами. Они просто замаскированы под функцию, а маскировка тонкая.

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. Прочтите предупреждения. Функция Integer генерирует предупреждение «Неинициализированная переменная». Строковая функция - нет. Это так ?

Почему разница? Поскольку строка является ARC-типом. Это означает, что строковая функция - это действительно процедура, а не функция. И это означает, что переменная Result инициализируется вызывающим абонентом вне видимой функции.Вы также можете заметить, что переменные chant меняются после вызова, потому что целочисленная функция является реальной функцией, а назначение-после вызова является реальной. Наоборот, строковая функция является иллюзорной, а также иллюзорным назначением-после вызова. Он выглядит как назначенный, но это не так.

5) Последняя вещь. Почему я поместил эту метку в свой код.

/// REMEMBER this line, it is important, I would explain later. 
      Result := TrimRight(Result); 

Точно из-за этого kōan выше. Вы, должно быть, читали мусор здесь. Вы, должно быть, использовали переменную «Результат», которую вы ранее не инициализировали. Вы, должно быть, ожидали, что ваш компилятор даст вам предупреждение в этой строке. Как это делает в ImpossibleI настоящая функция выше. Но это не так. Точно так же, как с ImpossibleS иллюзорной функцией. Это отсутствие предупреждения - это мертвая жертва иллюзии, которую создает здесь Delphi. Вы заметили бы, что ДОЛЖНО быть «неинициализированным var» предупреждением, но он пропал без вести, вы спросите себя, «кто инициализировал переменную, если это не моя функция», и вы поймете, что произошло с вами. ;-)

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; 

Эти две процедуры выглядят одинаково. Разница в S2. Это должен быть параметр OUT, а не параметр VAR (IN + OUT). Но Delphi терпит неудачу здесь только с вашей функцией. Delphi не может выполнять строковые OUT-параметры. Для сравнения, FreePascal (Lazarus, CodeTyphon) знает разницу и будет показывать предупреждение «неинициализированная переменная» для процедуры StringOut.

+0

Если мне не все равно, что пройденный аргумент аргумента« Результат »в функции вызываемого абонента не очищается в первая строка (Result = nil)? – user30478

+1

Вы не можете назначить NIL строковой переменной. Да, он будет очищен, если вы его назначите. Но вы НЕ назначили его. И это все. Если вы хотите, чтобы он был очищен, вы должны его очистить.Либо внутри функции, либо снаружи, но вы должны это сделать. –

+1

Понимание проблемы (строка возвращает фактически аргумент «var»), источник мусора (результат: = TrimRight (Result)), подсказка о освобождении XMLDoc и все основные вещи (подход koan, типы ARC, предупреждения компилятора и т. Д.) сделайте это ОТЛИЧНЫМ ответом. Это будет очень полезно для будущих читателей. – user30478

2

Но эта функция имеет странную проблему, которая запоминает входной сигнал, что означает, что объекты не были отпущены должным образом. Строка вывода имеет часть, полученную в результате предыдущего вызова, даже если вход отмечен как отличающийся.

Это потому String значения, которое возвращает function (а также других ARC управляемых типов, а также record, object и указатели метода) получает передается между вызывающим и вызываемым использованием скрытого var параметра, который NOT автоматически очищается, когда функция вводится, как вы ожидаете.

Этот код:

function tform1.nodeToSentence(nodeXml: string): string ; 
... 
readableSentence := nodeToSentence(mugList.Strings[i]); 

фактически то же самое, как этот код:

procedure tform1.nodeToSentence(nodeXml: string; var Result: string); 
... 
nodeToSentence(mugList.Strings[i], readableSentence); 

Вызов nodeToSentence() несколько раз, и вы потенциально добавляя все больше и больше текста, к тому же String переменной.

Внутри функции, необходимо вручную сбросить значение Result, прежде чем начать конкатенации новые значения к нему:

function tform1.nodeToSentence(nodeXml: string): string ; 
var 
    ... 
begin 
    Result := ''; // <-- add this! 
    ... 
end; 
+0

Реми, пожалуйста. 1) не редактируйте мой код. 2) не редактируйте его, пока я его редактирую. 3) не меняйте правильный код на сломанный. Присвоение переменной String нуль даже не компилируется! –

+3

** Только ** вещь, которую я изменил, заключалась в том, чтобы сделать 'Result: = nil' be' Result: = '' 'вместо этого. Как вы сами заявили: «* Вы не можете назначить NIL строковой переменной *», поэтому я просто исправил ошибку для вас. –

+0

Хорошо, я неправильно читаю журналы редактирования. Действительно, № 3 была моей ошибкой, извините. Но остальное держится, пока я делал длинное правление. SO внезапно подсказывает мне, что я не могу его сохранить. Это раздражало. Немного комментариев здесь хватило бы :-) –

 Смежные вопросы

  • Нет связанных вопросов^_^