* * *

Author Topic: Weird access violation while implementing Observer pattern  (Read 297 times)

Nevada Smith

  • Newbie
  • Posts: 2
Weird access violation while implementing Observer pattern
« on: March 18, 2017, 05:44:33 pm »
Hi,
I am trying to implement a simple Observer pattern. This ends in access violation at Observer.Free.
Please help and advise what am I doing wrong...

Additionally,
1. If Subject.AddSubscription(Observer); is removed, it works without any error.
2. I had faced the problem mentioned in http://forum.lazarus.freepascal.org/index.php?topic=36093.0 after installation and had to fix it manually.

Code: Pascal  [Select]
  1. program observertest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, fgl
  10.   { you can add units after this };
  11.  
  12. type
  13.  
  14.  
  15.   IUIObserver = interface
  16.     procedure NewTimer();
  17.   end;
  18.  
  19.   IUISubject = interface
  20.     procedure AddSubscription(aObserver: IUIObserver);
  21.     procedure RemoveSubscription(aObserver: IUIObserver);
  22.     //procedure NotifyObservers;
  23.  
  24.   end;
  25.  
  26.   TUIObserverList = specialize TFPGList<IUIObserver>;
  27.  
  28.   { TSubject }
  29.  
  30.   TSubject = class(TInterfacedObject, IUISubject)
  31.   public
  32.     constructor Create();
  33.     Destructor  Destroy; override;
  34.     procedure AddSubscription(aObserver: IUIObserver);
  35.     procedure RemoveSubscription(aObserver: IUIObserver);
  36.   end;
  37.  
  38.   { TObserver }
  39.  
  40.   TObserver = class(TInterfacedObject, IUIObserver)
  41.   public
  42.     constructor Create();
  43.     Destructor  Destroy; override;
  44.     procedure NewTimer();
  45.   end;
  46.  
  47. { TObserver }
  48.  
  49. constructor TObserver.Create;
  50. begin
  51.  
  52. end;
  53.  
  54. destructor TObserver.Destroy;
  55. begin
  56.   inherited Destroy;
  57. end;
  58.  
  59. procedure TObserver.NewTimer;
  60. begin
  61.  
  62. end;
  63.  
  64. { TSubject }
  65.  
  66. constructor TSubject.Create;
  67. begin
  68.  
  69. end;
  70.  
  71. destructor TSubject.Destroy;
  72. begin
  73.   inherited Destroy;
  74. end;
  75.  
  76. procedure TSubject.AddSubscription(aObserver: IUIObserver);
  77. begin
  78.  
  79. end;
  80.  
  81. procedure TSubject.RemoveSubscription(aObserver: IUIObserver);
  82. begin
  83.  
  84. end;
  85.  
  86. var
  87.   Subject: TSubject;
  88.   Observer: TObserver;
  89. begin
  90.   Subject := TSubject.Create();
  91.   Observer := TObserver.Create();
  92.  
  93.   Subject.AddSubscription(Nil);
  94.   Subject.AddSubscription(Observer);
  95.   Writeln('Hello');
  96.  
  97.   Observer.Free;
  98.   Subject.Free;
  99.  
  100. end.
  101.  

« Last Edit: March 18, 2017, 06:21:39 pm by Nevada Smith »

eny

  • Hero Member
  • *****
  • Posts: 1540
Re: Weird access violation while implementing Observer pattern
« Reply #1 on: March 18, 2017, 07:53:00 pm »
The default for using interfaces is COM interfaces hence they are reference counted and auto-freed.
'Subject.AddSubscription(Observer);' will trigger the destructor for Observer.
You are mixing the usage of objects and interfaces here.

See: http://www.freepascal.org/docs-html/prog/progsu37.html#x44-430001.2.37

Use {$INTERFACES CORBA} at the beginning of your program and the error goes away.
But it's best to have a good understanding of both models before starting to use interfaces.
Especially with COM interfaces there are some pitfalls and even an FPC bug that can cause havoc.
« Last Edit: March 18, 2017, 08:04:46 pm by eny »
All posts based on: Win10 (Win64); Lazarus 1.4.2 'stable' (#49524 win32) unless specified otherwise...

Nevada Smith

  • Newbie
  • Posts: 2
Re: Weird access violation while implementing Observer pattern
« Reply #2 on: March 18, 2017, 08:51:02 pm »
Wow! Thanks a million.

Zoran

  • Hero Member
  • *****
  • Posts: 1074
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Weird access violation while implementing Observer pattern
« Reply #3 on: March 19, 2017, 09:41:20 am »
But it's best to have a good understanding of both models before starting to use interfaces.

Nevada, you might find interesting reading this:
http://michalis.ii.uni.wroc.pl/~michalis/modern_pascal_introduction/modern_pascal_introduction.html#_interfaces

Which triggered an interesting discussion about interfaces in this forum topic: http://forum.lazarus.freepascal.org/index.php/topic,33095

It might help you, but it might just make you confused. ;)
« Last Edit: March 19, 2017, 09:43:27 am by Zoran »

Thaddy

  • Hero Member
  • *****
  • Posts: 2994
Re: Weird access violation while implementing Observer pattern
« Reply #4 on: March 19, 2017, 10:30:50 am »
The type of interface does matter, but indeed you are mixing class references with interface references and that is a deadly sin (As does that idiot in the artice mentioned, ignore  him).
Your code should look like this and with the COM style interfaces:
Code: Pascal  [Select]
  1. program observertest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  // Put the classes back here, they are perfectly OK.
  5. var
  6.   Subject: IUISubject;  // As interfaces,not as classes
  7.   Observer: IUIObserver;
  8. begin
  9.   Subject := TSubject.Create(); // create through interface
  10.   Observer := TObserver.Create();//create through interface
  11.  
  12.   Subject.AddSubscription(Nil);
  13.   Subject.AddSubscription(Observer);
  14.   Writeln('Hello');
  15.  // no need to free. That is done automatically. See heaptrc output
  16. end.

If you want to take advantage of COM interfaces your code should ALWAYS look like this.

Basically, you were on the right track apart from the variables declared as classes instead of interfaces and the use of free, which is superfluous with COM style interfaces.
Code: [Select]
// heaptrc output:
Hello
Heap dump by heaptrc unit
67 memory blocks allocated : 2518/2704
67 memory blocks freed     : 2518/2704
0 unfreed memory blocks : 0
True heap size : 196608 (160 used in System startup)
True free heap : 196448
« Last Edit: March 19, 2017, 10:45:24 am by Thaddy »

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus