سؤال

First of all, Hello for everybody and thanks for your help.

I'm trying to implement the Observer Pattern in Delphi using Interfaces, so an Object could be a Subject and an Observer at the same time.

I have a class that implements ISubject, with the following method:

procedure TSomeClass.Attach(const observer: IObserver);
var
  I: Integer;
begin
  if Fobservers = nil then
  begin
    Fobservers := TInterfaceList.Create;
  end;
  if Fobservers.IndexOf(Observer) < 0 then
    Fobservers.Add(Observer);
end;

I followed Joanna Carter's example at http://blogs.teamb.com/joannacarter/2004/06/30/690.

In the application, I instantiate an object that implements the IObserver, and attach it to the TSomeClass object (which implements ISubject as well).

Then I call the Notify method from the TSomeClass object and it works correctly. My problem occurs when I try to FreeAndNil my Observer object, because I get an Invalid Pointer Operation, even though I'm using 'const' in the parameter and when I reach the FreeAndNil line in debug mode, the object is properly assigned, with all properties set and with a random property changed inside the notify.

I noted that I couldn't Free my object anymore when I call this line:

Fobservers.Add(Observer);

If I comment this line, then I can free my object. The code inside the application looks like this:

procedure TfrmAlisson.Button2Click(Sender: TObject);
var
  locSomeClass: TSomeClass;
  locObserver: TSomeObserverClass;
  I: Integer;
begin
  locObserver:= TSomeObserverClass.create(394693);
  try
    locSomeClass:= TSomeClass.create(263151);
    try
      locSomeClass.Attach(locObserver);
      locSomeClass.NotifyObservers;
    finally
      FreeAndNil(locSomeClass);
    end;
    ShowMessage(IntToStr(locObserver.SomeProperty)); // This property is changed inside the notify
  finally
    locObserver.Free; // error
  end;
end;

I would like to know why adding the IObserver to the TInterfaceList causes this (I'm using Delphi 2009).

هل كانت مفيدة؟

المحلول

Your TSomeObserverClass is most likely inheriting from TInterfacedObject.

When you pass it in Attach it gets passed as IObserver and this is where the reference counting kicks in. The RefCount goes to 1 when it gets added to Fobservers and when you destroy locSomeClass and with it the Fobservers list it gets removed again which causes the RefCount to drop to 0. Then the instance behind the IObserver interface reference is being destroyed.

To show the problem here is the minimal code to reproduce it:

var
  obj: TInterfacedObject;
  list: TInterfaceList;
begin
  o := TInterfacedObject.Create;
  try
    list := TInterfaceList.Create;
    list.Add(o);
    list.Free;
  finally
    FreeAndNil(o);
  end;
end;

If you execute this you see the EInvalidError in the FreeAndNil which is caused because the instance was already destroyed by the automatic reference counting implemented in TInterfacedObject.

As already commented you should not mix object and interface references or inherit from a class that does not implement automatic reference counting.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top