/
YakuninAV
/
Convertors
Обзор
Документация
Войти
/
YakuninAV
/
Convertors
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
Core/WeakRefs.pas
258 строк
7 KB
YakuninAV
begin with gitflick
31 июл 2026, 15:21
31 июл 2026, 15:21
1dc0fc6
Код
Авторство
О чём код?
{ Трюки с интерфейсами в Delphi https://habr.com/ru/post/219685/ } /// Модуль для реализации механизма слабых [weak] ссылок unit WeakRefs; {$I mormot.defines.inc} interface uses SysUtils, Classes, SyncObjs; type /// Интерфейс слабой ссылки IWeakRef = interface(IInterface) ['{1C4EB35C-B3B5-4A4B-9831-D571C4482506}'] /// Получить интерфейс объекта, на который указывает ссылка function Get: IUnknown; /// Проверить, что объект, на который указывает ссылка, ещё существует function IsAlive: Boolean; /// Удалить объект, на который указывает ссылка procedure _Clean; end; /// Интерфейс для получения слабой ссылки IWeakly = interface(IInterface) ['{F1DFE67A-B796-4B95-ADE1-8AA030A7546D}'] /// Получить интерфейс слабой ссылки function WeakRef: IWeakRef; end; /// Массив слабых ссылок TWeakRefArr = array of IWeakRef; /// Интерфейс подписки IPublisher = interface(IInterface) ['{CDE9EE5C-021F-4942-A92A-39FC74395B4B}'] /// Добавить подписчика procedure Subscribe(const ASubscriber: IWeakly); /// Удалить подписчика procedure Unsubscribe(const ASubscriber: IWeakly); end; /// Класс реализующий получение интерфейса слабой ссылки TWeaklyInterfacedObject = class(TInterfacedObject, IWeakly) private FWeakRef: IWeakRef; public /// Деструктор destructor Destroy; override; /// Получить интерфейс слабой ссылки function WeakRef: IWeakRef; end; /// Базовый класс, реализующий интерфейс подписки TBasePublisher = class(TInterfacedObject, IPublisher) private FCount: Integer; FCS: TCriticalSection; FItems: TWeakRefArr; procedure Grow(const NeedCapacity: Integer); procedure Pack; protected function GetItems: TWeakRefArr; procedure SafePack; public constructor Create(const AThreadSafe: Boolean); destructor Destroy; override; procedure Subscribe(const ASubscriber: IWeakly); procedure Unsubscribe(const ASubscriber: IWeakly); end; /// Автопаблишер TAutoPublisher = packed record Publisher: IPublisher; class operator Add(const APublisher: TAutoPublisher; const ASubscriber: IWeakly): Boolean; class operator Subtract(const APublisher: TAutoPublisher; const ASubscriber: IWeakly): Boolean; end; /// Класс, реализующий интерфейс IWeakRef TWeakRef = class(TInterfacedObject, IWeakRef) private FOwner: Pointer; public /// Конструктор constructor Create(const AOwner: IUnknown); /// Получить интерфейс объекта, на который указывает ссылка function Get: IUnknown; /// Проверяет, что объект, на который указывает ссылка, ещё существует function IsAlive: Boolean; /// Удалить объект, на который указывает ссылка procedure _Clean; end; /// Функция возвращает true если объект на который ссылается weak ссылка - еще существует function IsAlive(const Ref: IWeakRef): Boolean; implementation function IsAlive(const Ref: IWeakRef): Boolean; begin Result := Assigned(Ref) and Ref.IsAlive; end; { TWeaklyInterfacedObject } destructor TWeaklyInterfacedObject.Destroy; begin inherited; if FWeakRef<>nil then FWeakRef._Clean; end; function TWeaklyInterfacedObject.WeakRef: IWeakRef; begin if FWeakRef = nil then FWeakRef:=TWeakRef.Create(Self); Result:=FWeakRef; end; { TBasePublisher } procedure TBasePublisher.Grow(const NeedCapacity: Integer); var Size: Integer; begin Size := Length(FItems); if Size < NeedCapacity then Begin if Size<8 then Size := 8; while Size<NeedCapacity do Size := Size*2; SetLength(FItems, Size); End; end; procedure TBasePublisher.Pack; var Offset, i: Integer; begin Offset:=0; for i:=0 to FCount-1 do begin if not IsAlive(FItems[i]) then Inc(Offset) else if Offset>0 then FItems[i-Offset] := FItems[i]; end; FCount := FCount-Offset; SetLength(FItems, FCount); end; function TBasePublisher.GetItems: TWeakRefArr; var i: Integer; begin if assigned(FCS) then begin FCS.Enter; try SetLength(Result, FCount); for i:=0 to FCount-1 do Result[i] := FItems[i]; finally FCS.Leave; end; end else Result := FItems; end; procedure TBasePublisher.SafePack; begin if assigned(FCS) then begin FCS.Enter; try Pack; finally FCS.Leave; end; end else Pack; end; constructor TBasePublisher.Create(const AThreadSafe: Boolean); begin if AThreadSafe then FCS := TCriticalSection.Create; end; destructor TBasePublisher.Destroy; begin FreeAndNil(FCS); inherited; end; procedure TBasePublisher.Subscribe(const ASubscriber: IWeakly); begin if Assigned(FCS) then begin FCS.Enter; try Grow(FCount + 1); FItems[FCount]:=ASubscriber.WeakRef; Inc(FCount); finally FCS.Leave; end; end else begin Grow(FCount+1); FItems[FCount]:=ASubscriber.WeakRef; Inc(FCount); end; end; procedure TBasePublisher.Unsubscribe(const ASubscriber: IWeakly); var i: Integer; begin if assigned(FCS) then begin FCS.Enter; try for i:=0 to FCount-1 do if FItems[i]=ASubscriber.WeakRef then FItems[i]:=nil; finally FCS.Leave; end; end else begin for i:=0 to FCount-1 do if FItems[i] = ASubscriber.WeakRef then FItems[i] := nil; end; end; { TWeakRef } constructor TWeakRef.Create(const AOwner: IUnknown); begin FOwner:=Pointer(AOwner); end; function TWeakRef.Get: IUnknown; begin Result:=IUnknown(FOwner); end; function TWeakRef.IsAlive: Boolean; begin Result:=Assigned(FOwner); end; procedure TWeakRef._Clean; begin FOwner:=nil; end; { TAutoPublisher } class operator TAutoPublisher.Add(const APublisher: TAutoPublisher; const ASubscriber: IWeakly): Boolean; begin APublisher.Publisher.Subscribe(ASubscriber); Result:=True; end; class operator TAutoPublisher.Subtract(const APublisher: TAutoPublisher; const ASubscriber: IWeakly): Boolean; begin APublisher.Publisher.Unsubscribe(ASubscriber); Result:=True; end; end.