Как я могу проверить, действительна ли ссылка на объект? - PullRequest
4 голосов
/ 28 января 2010

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

procedure TForm1.Button1Click(Sender: TObject);
  var form1 : TForm;
      ref2 : TControl;
begin
  form1 := TForm.Create(nil);
  form1.Name := 'CustomForm';
  form1.Parent := self; //Main Form

  form1.Show;

  ref2 := form1;
  showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
  freeandnil(form1);
  showmessage(ref2.ClassName+' - '+ref2.Name+' - '+BoolToStr(ref2.visible,true));
end;

Возвращается первое showmessage - «TForm - CustomForm - True» (как я и ожидал).

Второе возвращение показа сообщения - "TForm - - False". Я действительно надеялся на какое-то нарушение доступа, которое я мог бы затем перехватить и узнать, что ссылка недействительна.

В моем приложении мне нужно составить список случайных потомков TForm по мере их создания, а затем проверить, не исчезли ли они (или не видны). К сожалению, это система на основе плагинов, поэтому я могу изменить все эти формы, чтобы опубликовать сообщение «Я закончил».

Будет ли такой код безопасным для использования (при условии, что я действительно проверяю нарушения прав доступа)? У кого-нибудь есть идеи, что происходит?

Спасибо

Ответы [ 9 ]

6 голосов
/ 28 января 2010

Проблема в том, что с определенной вероятностью доступ к памяти все еще зарезервирован диспетчером памяти Delphi. В этом случае Windows не создает никаких нарушений прав доступа, потому что эта память принадлежит вам!

Одна возможность - переключиться на другой менеджер памяти Delphi, который может обнаружить использование освобожденных объектов. Например, FastMM4 имеет несколько проверок «гигиены памяти», которые очень полезны для отладки, но даже в этом случае вы не сразу обнаружите все эти ошибки.

Вы можете скачать FastMM4 с SourceForge .

6 голосов
/ 29 января 2010

Любой TComponent (например, TForm потомок) может зарегистрироваться для уведомлений, когда другие компоненты уничтожены.

В вашей форме звоните FreeNotification ( форма ) для каждой формы, о которой вы хотите получить уведомление об уничтожении. Затем в той же форме переопределите метод Notification () . При уничтожении любой формы (или другого компонента), для которой вы вызвали FreeNotification () , ваш метод Notification () будет вызываться с параметром Component , ссылающимся на форма и операция из opRemove .

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

2 голосов
/ 28 января 2010

* После 1001 *

freeandnil(form1);

диспетчер памяти Delphi просто помечает память, выделенную form1, как свободную, но все данные form1 все еще там, и к ним можно обращаться через ref2, пока диспетчер памяти не использует повторно освобожденную память для некоторых других объектов.

Вы не можете проверить этот путь, если ref2 ссылается на действительный объект или нет. Подобный код не может быть безопасным, на самом деле это ошибка. Если вы хотите получить 100% нарушение прав доступа, измените код следующим образом (здесь ref2 ^ = nil, если form1 освобожден):

procedure TForm1.Button1Click(Sender: TObject);
  var form1 : TForm;
      ref2 : ^TControl;
begin
  form1 := TForm.Create(nil);
  form1.Name := 'CustomForm';
  form1.Parent := self; //Main Form

  form1.Show;

  ref2 := @form1;
  showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
  freeandnil(form1);
  showmessage(ref2^.ClassName+' - '+ref2^.Name+' - '+BoolToStr(ref2^.visible,true));
end;
1 голос
/ 29 января 2010

Есть один очень интересный менеджер памяти. Он называется SafeMM: http://blogs.embarcadero.com/medington/2009/10/16/24839 Но все же это только для отладки.

1 голос
/ 28 января 2010

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

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

1 голос
/ 28 января 2010

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

TMyForm = class(TForm)
private
  //*** This is the reference to the singleton...
  FFormHandler: TFormHandler;
public
  ...
  //*** you might want to publish it as a property:
  property FormHandler: TFormHandler read FFormHandler write FFormHandler;
end;

Вы можете установить эту ссылку, например, при вызове конструктора:

TMyForm.Create(aFormHandler: TFormHandler; aOwner: TComponent)
begin
  FFormHandler := aFormHandler;
  inherited Create(aOwner);
end;

(Или вы можете установить поле извне сразу после создания формы, если не хотите изменять параметры конструктора).

Когда форма уничтожена, она уведомляет обработчик и говорит ему удалить форму из списка - что-то вроде этого:

TMyForm.Destroy(Sender: TObject);
begin
  FFormHandler.RemoveFromFormList(Self);
  inherited;
end;

(Детали отслеживания не включены в пример - например, потребуется метод "AddToFomList" или что-то подобное)

0 голосов
/ 03 сентября 2013

это так же просто, как сравнивать с NIL:

    // object declaration

Type object;

object = new Type();

...


// here you want to be sure of the existance of the object:

if (object <> nil )

  object.free;
0 голосов
/ 28 января 2010

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

  • У вас есть 1 способ сделать это проверка, является ли ссылка на объект до сих пор, что это должно быть глядя вверх VMT. Эта идея была впервые опубликовано Рэем Лишнером (который выступал за FreeAndNil именно по этой причине) и позже Hallvard Vassbotn : см. этот ТАК ответ .

  • Еще одно, лучшее, но вводящее значительное замедление, заключается в использовании FastMM4 в FullDebugmode, чтобы он мог заменить все освобожденные объекты экземпляром TFreeObject вместо простого освобождения памяти доступному пулу.

Обратите внимание , что оба метода не предотвращают ложное срабатывание, если другой экземпляр того же класса создается по тому же адресу памяти. Вы получаете действительный объект правильного типа, но не оригинальный. (Маловероятно в вашем случае, но возможно)

0 голосов
/ 28 января 2010

Если вы не можете проверить по-другому, вы можете использовать это как последнее средство ±

function IsValidClass( Cls: TClass ): Boolean;
var
    i: Integer;
begin
    for i := 0 to 99 do begin
        Result := ( Cls = TObject ); // note that other modules may have a different root TObject!
        if Result then Exit;
        if IsBadReadPtr( Cls, sizeof( Pointer ) ) then Break;
        if IsBadReadPtr( Pointer( Integer( Cls ) + vmtParent ), sizeof( Pointer ) ) then Break;
        Cls := Cls.ClassParent;
    end;
    Result := False;
end;

function IsValidObject( Obj: TObject ): Boolean;
begin
    Result := not IsBadReadPtr( Obj, sizeof( Pointer ) ) and IsValidClass( Obj.ClassType ) and not IsBadReadPtr( Obj, Obj.InstanceSize );
end;

IsBadReadPtr происходит из Windows.

...