TJclStringList вылетает на Free

Создайте простое приложение VCL:

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TForm1 = class(TForm)
   procedure FormDestroy(Sender: TObject);
   procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  JclStringLists;

var
  MyList1: TJclStringList;
  MyList2: TJclStringList;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  MyList1.Free;
  MyList2.Free;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyList1 := TJclStringList.Create;
  MyList2 := TJclStringList.Create;
  MyList1.LoadFromFile('C:\ONE.txt');
  MyList2.LoadFromFile('C:\TWO.txt');
  Self.Caption := Self.Caption + ' ' + IntToStr(MyList1.Count);
  Self.Caption := Self.Caption + ' ' + IntToStr(MyList2.Count);
end;

end.

Он падает в TForm1.FormDestroy обработчик событий при попытке освободить экземпляр объекта MyList1. Зачем?

1 ответ

Решение

TJclStringList является ссылочным типом (он объявлен в JCLStringLists.pas как type TJclStringList = class(TJclInterfacedStringList, IInterface, IJclStringList) и реализует оба _AddRef а также _Release для обработки ссылок), так что вам вообще не следует создавать их как объекты, и вы не должны их вручную освобождать - они будут автоматически освобождены при удалении ссылки на них. (Это также означает, что вы не должны объявлять их как глобальные переменные, потому что тогда вы не сохраняете контроль над их временем жизни.)

JclStringLists Модуль предоставляет несколько функций, которые будут правильно создавать экземпляр интерфейса для вас. Вы можете увидеть их в этом блоке, чуть выше implementation ключевое слово:

function JclStringList: IJclStringList; overload;
function JclStringListStrings(AStrings: TStrings): IJclStringList; overload;
function JclStringListStrings(const A: array of string): IJclStringList; overload;
function JclStringList(const A: array of const): IJclStringList; overload;
function JclStringList(const AText: string): IJclStringList; overload;

Правильный способ использования TJclStringList делать то, что вы хотите, это что-то вроде этого:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, JCLStringLists;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    MyList1, MyList2: IJCLStringList;  // Note I and not T in type.
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyList1 := JclStringList;
  MyList1.LoadFromFile('C:\Work\Data\FirstName.txt');
  MyList2 := JclStringList
  MyList2.LoadFromFile('C:\Work\Data\LastName.txt');

  // Only to demonstrate that both files got loaded by the code above.
  Self.Caption := Format('First: %d Last: %d', [MyList1.Count, MyList2.Count]);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  // Do NOT free the JclStringLists here - they will automatically be released when
  // the form is destroyed because the reference count will reach zero (as long as
  // you don't have any other references to those variables, which by putting them into
  // the private section is unlikely.
end;

end.
Другие вопросы по тегам