XPObserver.pas
上传用户:yjb1804
上传日期:2021-01-30
资源大小:3105k
文件大小:22k
源码类别:

Email服务器

开发平台:

Delphi

  1. unit XPObserver;
  2. {
  3.  $Source: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/Common/XPObserver.pas,v $
  4.  $Revision: 1.2 $
  5.  $Date: 2004/05/03 15:07:15 $
  6.  Last amended by $Author: pvspain $
  7.  $State: Exp $
  8.  XPObserver:
  9.  Interfaces and base classes to handle the mutual reference problem, wherein
  10.  two objects prevent each other from being destroyed. Each holds a
  11.  reference to the other, consequently neither's reference counts can reduce to
  12.  zero, and their destructors will never be called.
  13.  There are two commonly observed instances of this problem. The first is
  14.  Parent/Child, where the Parent is responsible for the lifetime of the Child.
  15.  Parent/Child is a particular case of the more general second scenario,
  16.  Observer/Subject, where there is no lifetime relationship between the parties.
  17.  There are two parties in Observer/Subject, an Observer, and the party being
  18.  observed, the Subject. The Observer must implement IXPObserver, and the Subject
  19.  must implement IXPSubject. The base class TXPSubject implements both IXPSubject
  20.  and IXPObserver, as this relationship is often chained - the Observer itself
  21.  has Observers and so on. IXPObserver is easy to otherwise implement, as it
  22.  has only one method, ReleaseSubject, which has a very simple implementation.
  23.  The Observer obtains a reference to Subject and registers interest by calling
  24.  Subject.AddObserver() and passing itself (as an IXPObserver or descendant
  25.  interface) and the Subject (as an IXPSubject or descendant interface) as
  26.  arguments. Optionally, the Observer can also pass some context-specific
  27.  information as Context (defaults to nil), which will be returned unaltered
  28.  in a later ReleaseSubject() callback on the Observer.
  29.  When the Subject is being destroyed, it will call Observer.ReleaseSubject()
  30.  on all registered Observers, passing itself (the Subject reference passed in
  31.  via Subject.AddObserver()) and the Observer-supplied Context argument
  32.  (defaults to nil) as the arguments.
  33.  The Observers, in response, must release their reference to the Subject. The
  34.  Subject then deletes the Observer from its list of Observers, releasing its
  35.  reference to the Observer in the process.
  36.  If an Observer wishes to initiate detachment from the Subject, it must call
  37.  Subject.DeleteObserver(), passing itself (IXPObserver), and any Context passed
  38.  earlier to Subject.AddObserver() as the argument. The Subject will respond by
  39.  calling Observer.ReleaseSubject, wherein the Observer must release its
  40.  reference to the Subject (Subject := nil).
  41.  In the Parent/Child scenario, both parties must implement IXPFamily, to
  42.  allow for hierarchical relationships. Use the base class TXPFamily for
  43.  parents and children, passing the parent in the constructor and, typically, no
  44.  further action is required. The interface provides accessor methods for the
  45.  Parent and any Children. The Parent can also accept non-Child observers, as
  46.  it derives from IXPSubject. 
  47.  Copyright (c) 2002,2003 by The Excellent Programming Company Pty Ltd
  48.  (Australia) (ABN 27 005 394 918).
  49.  Contact Paul Spain via email: paul@xpro.com.au
  50.  This unit is free software; you can redistribute it and/or
  51.  modify it under the terms of the GNU Lesser General Public
  52.  License as published by the Free Software Foundation; either
  53.  version 2.1 of the License, or (at your option) any later version.
  54.  This unit is distributed in the hope that it will be useful,
  55.  but WITHOUT ANY WARRANTY; without even the implied warranty of
  56.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  57.  Lesser General Public License for more details.
  58.  You should have received a copy of the GNU Lesser General Public
  59.  License along with this unit; if not, the license can be viewed at:
  60.  http://www.gnu.org/copyleft/lesser.html
  61.  or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  62.  Boston, MA  02111-1307  USA
  63.  }
  64. interface
  65. uses
  66.   XPInterfacedObject,   // TXPInterfacedObject
  67.   SyncObjs,             // TCriticalSection
  68.   Classes;              // IInterfaceList
  69. type
  70. /////////////////////////////////////////////////////////////////////////////
  71. //   Interface declarations
  72. /////////////////////////////////////////////////////////////////////////////
  73.   IXPSubject = interface;
  74.   IXPObserver = interface
  75.     ['{DF1C9798-E422-42B0-8534-26CC28214DFB}']
  76.     procedure ReleaseSubject(const Subject: IXPSubject;
  77.       const Context: pointer);
  78.   end;
  79.   // IXPSubject inherits from IXPObserver to enable observer chains and
  80.   // parent-child hierarchies, and provides a simple linkage for cascading
  81.   // notifications.
  82.   IXPSubject = interface(IXPObserver)
  83.     ['{D7E3FD5D-0A70-4095-AF41-433E7E4A9C29}']
  84.     function AddObserver(const Observer: IXPObserver;
  85.       const Subject: IXPSubject; const Context: pointer = nil): boolean;
  86.     function InsertObserver(const idx: integer; const Observer: IXPObserver;
  87.       const Subject: IXPSubject; const Context: pointer = nil): boolean;
  88.     function DeleteObserver(const Observer: IXPObserver;
  89.       const Context: pointer = nil): boolean;
  90.     procedure DeleteObservers;
  91.     function ObserverCount: integer;
  92.     function GetObserver(const idx: integer): IXPObserver;
  93.     property Observers[const idx: integer]: IXPObserver read GetObserver;
  94.     property Count: integer read ObserverCount;
  95.   end;
  96.   IXPFamily = interface(IXPSubject)
  97.     ['{D624A50F-F6D3-426C-BB74-2503FE654546}']
  98.     function GetParent: IXPFamily;
  99.     procedure SetParent(const AParent: IXPFamily);
  100.     property Parent: IXPFamily read GetParent write SetParent;
  101.     property Children[const idx: integer]: IXPObserver read GetObserver;
  102.   end;
  103.   PInterface = ^IInterface;
  104.   TXPReleaser = procedure(const LocalRef: PInterface) of object;
  105.   // IXPSubjects:
  106.   // Convenience interface to automate the management of subject/observer
  107.   // relationships. Typically, an observer class (which contains references to
  108.   // subjects) would contain a member instance of type IXPSubjects to
  109.   // manage its subjects.
  110.   IXPSubjects = interface(IXPObserver)
  111.     ['{BA5B61FE-DE3D-47A4-83D6-5AD555E391D5}']
  112.     // Add an interface to be managed, and an optional callback method
  113.     // <Releaser> to be called when that interface must be released
  114.     function AddSubject(const LocalRef: PInterface;
  115.       const Releaser: TXPReleaser = nil): boolean;
  116.     // Observer-initiated detachment from subject.
  117.     function DeleteSubject(const LocalRef: PInterface): boolean;
  118.     // Observer-initiated detachment from all subjects. Should be called
  119.     // from container class's destructor.
  120.     procedure Clear;
  121.   end;
  122.   
  123. /////////////////////////////////////////////////////////////////////////////
  124. //   Interface implementor declarations
  125. /////////////////////////////////////////////////////////////////////////////
  126.   PXPSubjectInfo = ^TXPSubjectInfo;
  127.   TXPSubjectInfo = record
  128.     AsPassed: PInterface;
  129.     AsSubject: pointer;
  130.     Releaser: TXPReleaser;
  131.   end;
  132.   TXPSubjects = class(TXPInterfacedObject, IXPObserver, IXPSubjects)
  133.   private
  134.     FSubjects: TList;
  135.     // Initialised to false by default
  136.     FDestroying: boolean;
  137.     function FindInterface(const LocalRef: PInterface;
  138.       out idx: integer): boolean;
  139.   protected
  140.     // IXPObserver implementation
  141.     procedure ReleaseSubject(const ASubject: IXPSubject;
  142.       const Context: pointer); virtual;
  143.     // IXPSubjects implementation
  144.     // We allow duplicate values, but not duplicate references, so pass by
  145.     // reference to ensure there are no duplicate references. Said another way,
  146.     // we can have more than one local reference (interface variable) to a
  147.     // subject, but each local reference can only be registered once.
  148.     function AddSubject(const LocalRef: PInterface;
  149.       const Releaser: TXPReleaser = nil): boolean;
  150.     // Observer initiated detachment from Subject. Pass by reference to
  151.     // ensure we get the right reference
  152.     function DeleteSubject(const LocalRef: PInterface): boolean;
  153.     procedure Clear;
  154.   public
  155.     constructor Create(const ADelegator: IInterface = nil);
  156.     destructor Destroy; override;
  157.   end;
  158.   TXPSubject = class(TXPSubjects, IXPSubject, IInterface)
  159.   private
  160.     FDeletingObservers: boolean;
  161.     function FindObserver(const Observer: IXPObserver;
  162.       const Context: pointer; out idx: integer): boolean;
  163.   protected
  164.     FSync: TCriticalSection;
  165.     FObservers: TList;
  166.     function SameContent(
  167.       const ObserverA, ObserverB: IXPObserver): boolean; virtual;
  168.     // IInterface partial reimplementation
  169.     function _Release: Integer; stdcall;
  170.     // IXPSubject implementation
  171.     function AddObserver(const Observer: IXPObserver;
  172.       const Subject: IXPSubject; const Context: pointer = nil): boolean;
  173.     function InsertObserver(const idx: integer; const Observer: IXPObserver;
  174.       const Subject: IXPSubject; const Context: pointer = nil): boolean;
  175.     function DeleteObserver(const Observer: IXPObserver;
  176.       const Context: pointer = nil): boolean;
  177.     procedure DeleteObservers;
  178.     function ObserverCount: integer;
  179.     function GetObserver(const idx: integer): IXPObserver;
  180.   public
  181.     constructor Create(const ADelegator: IInterface = nil);
  182.     destructor Destroy; override;
  183.   end;
  184.   TXPFamily = class(TXPSubject, IXPFamily)
  185.   protected
  186.     FParent: IXPFamily;
  187.     // Reimplement method to cascade request to children.
  188.     procedure ReleaseSubject(const Subject: IXPSubject;
  189.       const Context: pointer); override;
  190.     function GetParent: IXPFamily; virtual;
  191.     procedure SetParent(const AParent: IXPFamily); virtual;
  192.   public
  193.     constructor Create(const AParent: IXPFamily = nil;
  194.       const ADelegator: IInterface = nil);
  195.   end;
  196. implementation
  197. uses
  198.   SysUtils;
  199. const CVSID: string = '$Header: /cvsroot/dunit/dunit/Contrib/DUnitWizard/Source/Common/XPObserver.pas,v 1.2 2004/05/03 15:07:15 pvspain Exp $';
  200. /////////////////////////////////////////////////////////////////////////////
  201. //   TXPSubjects implementation
  202. /////////////////////////////////////////////////////////////////////////////
  203. constructor TXPSubjects.Create(const ADelegator: IInterface);
  204. begin
  205.   inherited Create(ADelegator);
  206.   FSubjects := TList.Create;
  207. end;
  208. destructor TXPSubjects.Destroy;
  209. begin
  210.   // Set object-scope state so we can distinguish from user-initiated
  211.   // detachment from subjects.
  212.   FDestroying := true;
  213.   Clear;
  214.   FSubjects.Free;
  215.   inherited Destroy;
  216. end;
  217. procedure TXPSubjects.Clear;
  218. var
  219.   idx: integer;
  220.   SubjectInfo: PXPSubjectInfo;
  221. begin
  222.   // Disconnect from all subjects
  223.   for idx :=  FSubjects.Count - 1 downto 0 do
  224.   begin
  225.     SubjectInfo := PXPSubjectInfo(FSubjects[idx]);
  226.     // This call will consequentially invoke self.ReleaseSubject which will
  227.     // delete the FSubjects item referenced by SubjectInfo. Hence, next
  228.     // iteration we will be dealing with the new last item in FSubjects.
  229.     // We could just deal with FSubjects[FSubjects.Count - 1] and avoid the
  230.     // loop counter, idx, but there is potential for an infinite loop if the
  231.     // subject doesn't call back into self.ReleaseSubject
  232.     IXPSubject(SubjectInfo^.AsSubject).DeleteObserver(self,
  233.       SubjectInfo^.AsPassed);
  234.    end;
  235.   // Taking a pessimistic view, clean up any subjects who haven't responded
  236.   while FSubjects.Count > 0 do
  237.   begin
  238.     // Drop local reference to subject - don't call Releaser at this point
  239.     // as we're probably in a minefield of dangling references
  240.     PXPSubjectInfo(FSubjects[0])^.AsPassed^ := nil;
  241.     // Finalise FSubjects entry
  242.     System.Dispose(FSubjects[0]);
  243.     FSubjects.Delete(0);
  244.     // FSubjects.Count decrements and any remainder move up one slot in list
  245.   end;
  246. end;
  247. function TXPSubjects.AddSubject(const LocalRef: PInterface;
  248.   const Releaser: TXPReleaser): boolean;
  249. var
  250.   ASubject: IXPSubject;
  251.   SubjectInfo: PXPSubjectInfo;
  252.   idx: integer;
  253. begin
  254.   // We allow duplicate values but not duplicate references, so check for
  255.   // matching reference
  256.   Result := System.Assigned(LocalRef)
  257.     and SysUtils.Supports(LocalRef^, IXPSubject, ASubject)
  258.     and not FindInterface(LocalRef, idx);
  259.   if Result then
  260.   begin
  261.     // Setup new item for FSubjects
  262.     System.New(SubjectInfo);
  263.     // Store ASubject as pointer to leave ref count unaffected
  264.     SubjectInfo^.AsPassed := LocalRef;
  265.     SubjectInfo^.AsSubject := pointer(ASubject);
  266.     SubjectInfo^.Releaser := Releaser;
  267.     // Add new item
  268.     FSubjects.Add(SubjectInfo);
  269.                                                                                                              
  270.     // Release our local reference *before* AddObserver() call to avoid
  271.     // Subject.DeleteObservers call as ASubject goes out of scope
  272.     ASubject := nil;
  273.     // Register self.ReleaseSubject with Subject, using supplied reference
  274.     // as Context argument. We will use this for identification in
  275.     // ReleaseSubject callback
  276.     Result := IXPSubject(SubjectInfo^.AsSubject).AddObserver(self,
  277.       IXPSubject(SubjectInfo^.AsSubject), LocalRef);
  278.   end;
  279. end;
  280. function TXPSubjects.FindInterface(const LocalRef: PInterface;
  281.   out idx: integer): boolean;
  282. begin
  283.   idx := FSubjects.Count - 1;
  284.   while (idx >= 0)
  285.     and (PXPSubjectInfo(FSubjects[idx])^.AsPassed <> LocalRef) do
  286.     System.Dec(idx);
  287.   Result := idx >= 0;
  288. end;
  289. function TXPSubjects.DeleteSubject(const LocalRef: PInterface): boolean;
  290. var
  291.   idx: integer;
  292. begin
  293.   Result := System.Assigned(LocalRef) and FindInterface(LocalRef, idx);
  294.   if Result then
  295.     // We have a match - detach from subject
  296.     // ReleaseSubject will be called back consequently
  297.     Result := IXPSubject(PXPSubjectInfo(
  298.       FSubjects[idx])^.AsSubject).DeleteObserver(self, LocalRef);
  299. end;
  300. procedure TXPSubjects.ReleaseSubject(const ASubject: IXPSubject;
  301.   const Context: pointer);
  302. var
  303.   idx: integer;
  304.   SubjectInfo: TXPSubjectInfo;
  305. begin
  306.   // ASubject not used here. Subjects are keyed on Context, which is address of
  307.   // subject reference held locally. See AddSubject()
  308.   if FindInterface(PInterface(Context), idx) then
  309.   begin
  310.     SubjectInfo := PXPSubjectInfo(FSubjects[idx])^;
  311.     // Clean up FSubjects entry
  312.     System.Dispose(FSubjects[idx]);
  313.     FSubjects.Delete(idx);
  314.     if (@SubjectInfo.Releaser = nil)
  315.       or (FDestroying and not(TMethod(SubjectInfo.Releaser).Data = self)) then
  316.       // If we are in the context of our own destructor, and the supplied
  317.       // Releaser is not a method of this object, then the supplied
  318.       // Releaser may well be invalid at this point (as interface data members
  319.       // are released after containing destructor body executes), so just drop
  320.       // the reference.
  321.       SubjectInfo.AsPassed^ := nil
  322.     else
  323.       // Fire the supplied Releaser with the argument passed to AddSubject(),
  324.       // in turn passed by reference to Releaser
  325.       SubjectInfo.Releaser(SubjectInfo.AsPassed);
  326.   end;
  327. end;
  328. /////////////////////////////////////////////////////////////////////////////
  329. //   TXPSubject implementation
  330. /////////////////////////////////////////////////////////////////////////////
  331. type
  332.   PXPObserverInfo = ^TXPObserverInfo;
  333.   TXPObserverInfo = record
  334.     Observer: IXPObserver;
  335.     Subject: pointer;
  336.     Context: pointer;
  337.   end;
  338. constructor TXPSubject.Create(const ADelegator: IInterface);
  339. begin
  340.   inherited;
  341.   // FDeletingObservers initialised to false by default
  342.   FSync := TCriticalSection.Create;
  343.   FObservers := TList.Create;
  344. end;
  345. destructor TXPSubject.Destroy;
  346. begin
  347.   FObservers.Free;
  348.   FSync.Free;
  349.   inherited;
  350. end;
  351. function TXPSubject.SameContent(
  352.   const ObserverA, ObserverB: IXPObserver): boolean;
  353. begin
  354.   Result := ObserverA = ObserverB;
  355. end;
  356. function TXPSubject.FindObserver(const Observer: IXPObserver;
  357.   const Context: pointer; out idx: integer): boolean;
  358. begin
  359.   idx := FObservers.Count - 1;
  360.   while (idx >= 0)
  361.     and not (SameContent(PXPObserverInfo(FObservers[idx])^.Observer, Observer)
  362.       and (PXPObserverInfo(FObservers[idx])^.Context = Context)) do
  363.     System.Dec(idx);
  364.   Result := idx >= 0;
  365. end;
  366. function TXPSubject.AddObserver(const Observer: IXPObserver;
  367.   const Subject: IXPSubject; const Context: pointer): boolean;
  368. begin
  369.   // InsertObserver is a synchronised method but we need to isolate call on
  370.   // ObserverCount to same calling context, so synchronise both calls
  371.   FSync.Enter;
  372.   try
  373.     Result := InsertObserver(ObserverCount, Observer, Subject, Context);
  374.   finally
  375.     FSync.Leave;
  376.   end;
  377. end;
  378. function TXPSubject.ObserverCount: integer;
  379. begin
  380.   Result := FObservers.Count;
  381. end;
  382. function TXPSubject.InsertObserver(const idx: integer;
  383.   const Observer: IXPObserver; const Subject: IXPSubject;
  384.   const Context: pointer): boolean;
  385. var
  386.   jdx: integer;
  387.   ObserverInfo: PXPObserverInfo;
  388. begin
  389.   FSync.Enter;
  390.   try
  391.     // No duplicates - check for prior entry
  392.     // Check for range error
  393.     Result := not FindObserver(Observer, Context, jdx)
  394.       and (idx <= FObservers.Count) and (idx >= 0);
  395.     if Result then
  396.     begin
  397.       System.New(ObserverInfo);
  398.       ObserverInfo^.Observer := Observer;
  399.       ObserverInfo^.Subject := pointer(Subject);
  400.       ObserverInfo^.Context := Context;
  401.       FObservers.Insert(idx, ObserverInfo);
  402.     end;
  403.   finally
  404.     FSync.Leave;
  405.   end;
  406. end;
  407. function TXPSubject.DeleteObserver(const Observer: IXPObserver;
  408.   const Context: pointer): boolean;
  409. var
  410.   idx: integer;
  411.   ObserverInfo: TXPObserverInfo;
  412. begin
  413.   FSync.Enter;
  414.   try
  415.     // Check for existence or prior removal
  416.     Result := FindObserver(Observer, Context, idx);
  417.     if Result then
  418.     begin
  419.       // Need local ref after deletion from list. Order of Delete() &
  420.       // ReleaseSubject() is important here for correct functioning of _Release
  421.       // ...***DON'T*** refactor this method!!
  422.       ObserverInfo := PXPObserverInfo(FObservers[idx])^;
  423.       // Release our (list) reference to observer
  424.       PXPObserverInfo(FObservers[idx])^.Observer := nil;
  425.       System.Dispose(FObservers[idx]);
  426.       FObservers.Delete(idx);
  427.     end;
  428.     // Exit critical section here as we now have local vars only (thread-safe)
  429.     // and call to ReleaseSubject below on last reference will leave FSync
  430.     // invalid (destroyed).
  431.   finally
  432.     FSync.Leave;
  433.   end;
  434.   // Notify Observer to release reference to us. This will result in
  435.   // a call to TXPSubject._Release.
  436.   if Result then
  437.     ObserverInfo.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
  438.       ObserverInfo.Context);
  439. end;
  440. function TXPSubject.GetObserver(const idx: integer): IXPObserver;
  441. begin
  442.   if (idx < 0) or (idx >= FObservers.Count) then
  443.     Result := nil
  444.   else
  445.     Result := PXPObserverInfo(FObservers[idx])^.Observer;
  446. end;
  447. function TXPSubject._Release: Integer;
  448. begin
  449.   FSync.Enter;
  450.   try
  451.     // If this is the last reference excepting observers,
  452.     // then drop the observers - save last reference so FSync is still valid
  453.     if (FRefCount = FObservers.Count + 1) and (not FDeletingObservers) then
  454.       DeleteObservers;
  455.   finally
  456.     FSync.Leave;
  457.   end;
  458.   Result := inherited _Release;
  459. end;
  460. procedure TXPSubject.DeleteObservers;
  461. var
  462.   idx: integer;
  463.   ObserverInfo: PXPObserverInfo;
  464. begin
  465.   FDeletingObservers := true;
  466.   // Count *down* to allow for side-effect of loop actions -
  467.   // referenced item will be deleted from list, and remainder will move down
  468.   // one slot.
  469.   for idx := FObservers.Count - 1 downto 0 do
  470.   begin
  471.     ObserverInfo := FObservers[idx];
  472.     // Notify Observer to release reference to Subject
  473.     ObserverInfo^.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
  474.       ObserverInfo^.Context);
  475.     // Release our (list) reference to Observer
  476.     ObserverInfo^.Observer := nil;
  477.     System.Dispose(ObserverInfo);
  478.     FObservers.Delete(idx);
  479.   end;
  480.   FDeletingObservers := false;
  481. end;
  482. /////////////////////////////////////////////////////////////////////////////
  483. //   TXPFamily implementation
  484. /////////////////////////////////////////////////////////////////////////////
  485. // Parent creates child, passing itself in to child's constructor.
  486. constructor TXPFamily.Create(const AParent: IXPFamily;
  487.   const ADelegator: IInterface);
  488. begin
  489.   inherited Create(ADelegator);
  490.   SetParent(AParent);
  491. end;
  492. function TXPFamily.GetParent: IXPFamily;
  493. begin
  494.   Result := FParent;
  495. end;
  496. procedure TXPFamily.SetParent(const AParent: IXPFamily);
  497. var
  498.   ACopy: IXPFamily;
  499. begin
  500.   // We can re-parent a child with this method
  501.   if AParent <> FParent then
  502.   begin
  503.     // Undo previous association
  504.     if System.Assigned(FParent) then
  505.     begin
  506.       ACopy := FParent;
  507.       // Release Parent (explicitly) first, since we don't want ReleaseSubject
  508.       // side effects (DeleteObservers)
  509.       FParent := nil;
  510.       ACopy.DeleteObserver(self);
  511.       // ACopy will be released after we exit procedure (ACopy scope boundary)
  512.     end;
  513.     // Now bind to new parent
  514.     FParent := AParent;
  515.     // Check for nil assignment
  516.     if System.Assigned(FParent) then
  517.       FParent.AddObserver(Self, FParent);
  518.   end;
  519. end;
  520. procedure TXPFamily.ReleaseSubject(const Subject: IXPSubject;
  521.   const Context: pointer);
  522. begin
  523.   if (Subject = FParent) and System.Assigned(FParent) then
  524.   begin
  525.     // We don't need to detach from parent's observer list, as the *initiator*
  526.     // of a detachment is responsible for this...
  527.     // ( see Parent's DeleteObservers() implementation for an example )
  528.     // Release reference to parent
  529.     FParent := nil;
  530.     // Parent-child relationship for lifetime - this parent releases all
  531.     // *its* children (observers of parent)
  532.     DeleteObservers;
  533.   end
  534.   else
  535.     inherited ReleaseSubject(Subject, Context);
  536. end;
  537. end.