2015-03-01 4 views
3

Код, который выглядит примерно как следующая память утечек для каждого экземпляра строки в каждом созданном TMyRecord. Предполагаю, что я должен посетить каждую запись и бесплатно ее освободить - можно ли это сделать без заполнения каждой отдельной строки?TList типов записей утечка памяти

function TMyForm.RecordFromThing(thing): TMyRecord; 
begin 
    result.StringVal1 = thing.SomeProperty; 
    result.StringVal2 = thing.SomeOtherProperty; 
end; 

function TMyForm.RecordsFromItems: TList<TMyRecord>; 
begin 
    result := TList<TMyRecord>.Create; 
    for thing in things do 
    begin 
    result.Add(RecordFromThing(thing)); 
    end; 
end; 

procedure TMyForm.Button1Click(Sender: TObject); 
var Items: TList<TMyRecord; 
begin 

    Items := RecordsFromItems; 
    try 
    //Stuff 
    finally 
    // What goes here to free those records? 
    Items.Clear; 
    Items.Free; 
    end 

end; 

Согласно запросу Дэвида, здесь приведен фактический код, вырезанный и вставленный из моего приложения. Если я запустил Button1Click, то FastMM сообщит об утечках строк.

type TPremiumPaymentInstruction = record 
    SupplierID: string; 
    FirstName: string; 
    LastName: string; 
    PolicyNo: string; 
    CarrierName: string; 
    PayAmount: string; 
    DueDate: string; 
    PayMethod: string; 
    Comments: string; 
    Payee: string; 
    Address: string; 
    Address2: string; 
    City: string; 
    State: string; 
    Zip: string; 
    InRe: string; 
    BankName: string; 
    BankABA: string; 
    AccountName: string; 
    AccountNo: string; 
    CreditTo: string; 
    end; 

function TPremiumPaymentManager.RecordFromItem(Item: TListItem): TPremiumPaymentInstruction; 
var PolicyID: integer; 
    Instructions: TDataSet; 

    function FormatNote(PolicyNo, First, Last: string): string; 
    begin 
     result := 'Policy# ' + PolicyNo + '; Insured Name: ' + Last + ' ' + First; 
    end; 

begin 

    FillChar(result, SizeOf(result), 0); 

    result.SupplierID := Item.Caption; 
    result.FirstName := Item.SubItems[INSURED_FIRST_NAME_COLUMN]; 
    result.LastName := Item.SubItems[INSURED_LAST_NAME_COLUMN]; 
    result.PolicyNo := Item.SubItems[POLICY_NUMBER_COLUMN]; 
    result.CarrierName := Item.SubItems[CARRIER_NAME_COLUMN]; 
    result.PayAmount := Item.SubItems[ACTUAL_COLUMN]; 
    result.DueDate  := Item.SubItems[DATE_DUE_COLUMN]; 
    result.PayMethod := Item.SubItems[PAYMENT_METHOD_COLUMN]; 

    PolicyID := GetSingleValue('SELECT PolicyID FROM PremiumsDue WHERE PremiumID = :PremiumID;', [(Item as TAdvListItem).KeyValue], 0); 
    Instructions := GetDS('SELECT I.* FROM Policies P INNER JOIN CarrierPaymentInstructions I ON P.CarrierID = I.CarrierID AND P.PaymentInstruction = I.InstructionDescription WHERE P.PolicyID = ?;', [PolicyID]); 

    try 

    Instructions.Open; 
    Instructions.First; 

    if result.PayMethod = 'Check' then 
     begin 
     result.Comments := Item.SubItems[PAYMENT_NOTE_COLUMN]; 
     result.Payee := Instructions.FieldByName('PayTo').AsString; 
     result.Address := Instructions.FieldByName('Address1').AsString; 
     result.Address2 := Instructions.FieldByName('Address2').AsString; 
     result.City  := Instructions.FieldByName('City').AsString; 
     result.State := Instructions.FieldByName('State').AsString; 
     result.Zip  := Instructions.FieldByName('ZipCode').AsString; 
     result.InRe  := FormatNote(result.PolicyNo, result.FirstName, result.LastName); 
     end 
    else 
     begin 
     result.BankName := Instructions.FieldByName('PayTo').AsString; 
     result.BankABA  := Instructions.FieldByName('ABANumber').AsString; 
     result.Address2 := Instructions.FieldByName('Address2').AsString; 
     result.AccountName := Instructions.FieldByName('AccountName').AsString; 
     result.AccountNo := Instructions.FieldByName('AccountNumber').AsString; 
     result.CreditTo := FormatNote(result.PolicyNo, result.FirstName, result.LastName); 
     end; 

    finally 

    Instructions.Free; 

    end; 

end; 

function TPremiumPaymentManager.RecordsFromItems: TList<TPremiumPaymentInstruction>; 
var item: TListItem; 
begin 

    result := TList<TPremiumPaymentInstruction>.Create; 

    for item in lvPremiums.Items do 
    begin 
    if (not (Item as TAdvListItem).Strikeout) and (Item.SubItems[ACTUAL_COLUMN] <> '') then 
     result.Add(RecordFromItem(item)); 
    end; 

end; 

procedure TPremiumPaymentManager.Button1Click(Sender: TObject); 
var Items: TList<TPremiumPaymentInstruction>; 
begin 

    Items := RecordsFromItems; 
    Items.Clear; 
    Items.Free; 

end; 
+0

В вопрос добавлен раздел «Вырезать-вставить» из моего кода ошибки. –

+0

Используйте 'result: = Default (TPremiumPaymentInstruction)' для инициализации вашей записи. –

ответ

4

Строки управляются типами. Компилятор берет на себя ответственность за их распределение и освобождение. Компилятор пишет код, который поддерживает подсчет ссылок на строковые данные. Когда счетчик ссылок будет равен нулю, память будет освобождена.

Таким образом, вам не нужно освобождать строки или выполнять какое-либо явное управление. Любая попытка сделать это, скорее всего, столкнется с работой, выполняемой компилятором. Например, если вы выполняете необработанный доступ к памяти в строковой переменной, пишите с вызовом FillChar, тогда подсчет ссылок будет обходить.

Код в вашем вопросе, будучи неполным и по частям, не компилируемым, по существу прекрасен. Когда вы закончите свой TList<TMyRecord>, просто освободите его. Компилятор будет генерировать код для выпуска всех ссылок. Не нужно даже очищать его.

List := TList<TMyRecord>.Create; 
try 
    List.Add(...); 
    // etc. 
finally 
    List.Free; 
end; 

Это все, что вам нужно.

Поскольку код в вашем вопросе по существу такой же, как и этот, я делаю вывод, что причина утечки в другом месте.


Что вы знаете, что ваша правка содержит этот призыв сказать-сказку FillChar. Заменить это на

Result := Default(TPremiumPaymentInstruction); 

Вам всегда нужно инициализировать возвращаемые значения. Даже возвращаемые значения, которые управляются типами, которые часто инициализируются. Но не тогда, когда вызов выполняется внутри цикла for в соответствии с вашим. Идите фигуру. Во всяком случае, всегда инициализируйте возвращаемые значения. И Default(T) - твой друг.

+0

Я вырезал и вставил фактический код в вопрос. Обратите внимание, что я * am * использую FillChar для каждой записи, так как без этой инициализации я получал строковые указатели, по-видимому, повторяющиеся от записи к записи. –

+0

Это твоя проблема. Я обновил решение. –

+0

Спасибо, работая сейчас. У меня слабый проблеск, почему это провалилось, но только слабый. Посмотрите на это позже, чтобы узнать, могу ли я понять, почему. , , –

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