我很难理解为什么 XPObserver.pas 中的 TXPSubject.DeleteObserver 和 TXPSubject.DeleteObservers 的实现不同的原因?具体来说,“Dispose”和“ReleaseSubject”调用的不同顺序,以及不同顺序对 TXPSubject.DeleteObserver 很重要的原因。有问题的代码被提取并显示在下面。该文件可从 DUnit 获得。
function TXPSubject.DeleteObserver(const Observer: IXPObserver;
const Context: pointer): boolean;
var
idx: integer;
ObserverInfo: TXPObserverInfo;
begin
FSync.Enter;
try
// Check for existence or prior removal
Result := FindObserver(Observer, Context, idx);
if Result then
begin
// Need local ref after deletion from list. Order of Delete() &
// ReleaseSubject() is important here for correct functioning of _Release
// ...***DON'T*** refactor this method!!
ObserverInfo := PXPObserverInfo(FObservers[idx])^;
// Release our (list) reference to observer
PXPObserverInfo(FObservers[idx])^.Observer := nil;
System.Dispose(FObservers[idx]);
FObservers.Delete(idx);
end;
// Exit critical section here as we now have local vars only (thread-safe)
// and call to ReleaseSubject below on last reference will leave FSync
// invalid (destroyed).
finally
FSync.Leave;
end;
// Notify Observer to release reference to us. This will result in
// a call to TXPSubject._Release.
if Result then
ObserverInfo.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
ObserverInfo.Context);
end;
procedure TXPSubject.DeleteObservers;
var
idx: integer;
ObserverInfo: PXPObserverInfo;
begin
FDeletingObservers := true;
// Count *down* to allow for side-effect of loop actions -
// referenced item will be deleted from list, and remainder will move down
// one slot.
for idx := FObservers.Count - 1 downto 0 do
begin
ObserverInfo := FObservers[idx];
// Notify Observer to release reference to Subject
ObserverInfo^.Observer.ReleaseSubject(IXPSubject(ObserverInfo.Subject),
ObserverInfo^.Context);
// Release our (list) reference to Observer
ObserverInfo^.Observer := nil;
System.Dispose(ObserverInfo);
FObservers.Delete(idx);
end;
FDeletingObservers := false;
end;