утечка памяти при использовании TInterfacedObject в качестве ссылки на интерфейс в зависимых объектах [дубликат]

Замена одинарных кавычек:

function JavaScriptEncode(text){
    text = text.replace(/'/g,''')
    // More encode here if required

    return text;
}
6
задан Johan 10 May 2016 в 04:55
поделиться

4 ответа

Здесь вы решаете неправильную проблему.

Ваша актуальная проблема заключается не в сильных слабых ссылках, а в том, как можно улучшить ваше решение. Ваша проблема не в , как достичь , но в , чего вы добиваетесь (хотите достичь) .

Чтобы напрямую задать ваши вопросы в первую очередь:

  • "TContainedObject (FObj2) .Free;" немного пахнет, но у меня нет лучшего решения, так как мне нужно использовать интерфейс для ссылки на TObject2 (продуктивный код содержит несколько наследований с этой целью). Любые идеи по его очистке?

Здесь вы не можете многое сделать. Вы должны называть Free на FObj2, потому что TContainedObject не управляемый сам класс.

  • вы легко забываете объявить все ссылки между двумя классами как слабые и ..

Здесь вы ничего не можете сделать. Он поставляется с территорией. Если вы хотите использовать ARC, вам нужно подумать о круговых ссылках.

  • аналогичная проблема начинает подниматься с большим количеством классов: с TObject3, на который ссылается одна и ссылается на другую: память протечь. Я мог бы справиться с этим, позволив ему спуститься с TContainedObject, но с устаревшим кодом это может быть непростой задачей.

Вы тоже не можете много сделать. Если ваш дизайн действительно то, что вы хотите иметь, тогда вам просто придется иметь дело со своими сложностями.


Теперь вернемся к тому, почему у вас возникают проблемы в первую очередь.

То, что вы хотите достичь (и вы сделали это с вашим примером кода), поддерживает целостность всей иерархии объектов, захватывая любые ссылки на объекты внутри этой иерархии.

Чтобы перефразировать, вы имеете Form и Button на нем, и вы хотите сохранить Form живым, что-то содержит Button (поскольку Button сам не будет функционировать). Затем вы хотите добавить Edit к этому Form и снова сохранить все живое, если что-то схватит Edit.

У вас здесь мало вариантов.

  • Держите этот сломанный дизайн и живите с вашим решением, потому что у вас слишком много кода, и изменения будут болезненными. Если вы это сделаете, имейте в виду, что это в конечном счете сломанный дизайн и не пытайтесь повторить его где-либо еще.
  • Если у вас есть иерархия, где TObject1 является корневым классом, который содержит все остальное, то рефакторируйте его и наследовать TObject2 из TInterfacedObject, чтобы иметь свой собственный подсчет ссылок и не захватывать ссылки на FObj2. Вместо этого возьмите экземпляр root TObject1 и передайте это, если вам действительно нужно.
  • Это вариация второго подхода. Если TObject1 не является корневым классом, создайте дополнительный класс-оболочку, содержащий все экземпляры, которые вам нужны, и передайте их.

Последние два решения далеки от совершенства, и они не имеют дело с факт, что у вас, вероятно, есть классы, которые слишком много или похожи. Но как бы ни был плохой код, он даже не приближается к вашему текущему решению. И со временем вы можете медленно менять и улучшать эти решения гораздо проще, чем с помощью вашего текущего.

0
ответ дан Dalija Prasnikar 25 August 2018 в 21:57
поделиться

Если вы хотите, чтобы оба объекта были живыми или мертвыми вместе, они, безусловно, являются одним единственным объектом. Хорошо, я понимаю, что оба могут быть разработаны разными людьми, поэтому я бы сделал их обоими членами одного супер-объекта, который подсчитан ссылкой, например

type
  TSuperobject = class( TInterfaceObject, IObject1, iObject2 )
  private
    fObject1 : TObject1;
    fObject2 : TObject2;
  public
    constructor Create;
    destructor Destroy;
    function GetObject2: IObject2;
    etc.
  end;

etc.

. Детали должны быть очевидны. Любая ссылка на object1 или object2 должна ссылаться на объект-владелец (superobject.object1 и т. Д.), Поэтому сами объекты1 и object2 не нуждаются в подсчете ссылок - то есть они могут быть обычными объектами, а не сопряженными объектами, но на самом деле это не имеет значения если они подсчитаны, потому что владелец всегда будет добавлять 1 к счетчику ссылок (в этом случае вам может не понадобиться деструктор в суперобъекте). Если вы покидаете объекты object1 и object2 в качестве ссылочных объектов, их привязанность друг к другу становится слабой.

0
ответ дан Dsm 25 August 2018 в 21:57
поделиться

Не использовать небезопасные [unsafe] не следует использовать в обычном коде. Это действительно взлом для используемого, если вы не хотите, чтобы компилятор делал подсчет ссылок на интерфейсы.

Используйте слабый вместо этого. Если по какой-то причине вы должны иметь круговые ссылки, используйте атрибут [weak] в одной из ссылок и объявляйте другую ссылку как обычно.

В вашем примере это будет выглядеть так:

  TParent = class(TInterfacedObject, IParent)
    FChild: IChild;   //normal child
    constructor Create;
    function GetObject2: IChild;
  end;

  TChild = class(TContainedObject, IChild)
    //reference from the child to the parent, always [weak] if circular.
    [weak] FObj1: IParent;   
    constructor Create(const aObj1: IParent);
  end;

Теперь нет необходимости делать что-то особенное в деструкторах, поэтому их можно опустить. Компилятор отслеживает все слабые ссылки и устанавливает их в нуль, когда количество ссылок ссылочного интерфейса достигает нуля. И все это делается поточно-безопасным способом. Однако сама слабая ссылка не увеличивает счетчик ссылок.

Когда использовать небезопасно Это противоречит небезопасной ссылке, где не происходит никакого отслеживания и никакого подсчета ссылок.

Вы использовали бы ссылку [unsafe] для сопряженного типа, который является одиночным, или тот, у которого отключен подсчет ссылок. Здесь число ссылок фиксировано в -1 в любом случае, поэтому вызов addref и release является ненужным служебным. Помещение [unsafe] устраняет эти глупые накладные расходы. Если ваши интерфейсы не переопределяют _addref и _release, не используйте [unsafe].

Pre Berlin альтернативный Pre Berlin нет атрибута [weak] вне компиляторов NexGen. Если вы работаете в Сиэтле, 2010 год или что-то еще между следующим кодом, он будет почти таким же. Хотя я не уверен, что этот код не может стать жертвой условий гонки в многопоточном коде. Если это вызывает беспокойство, вы можете поднять флаг, и я буду исследовать.

  TParent = class(TInterfacedObject, IParent)
    FChild: IChild;   //normal child
    constructor Create;
    function GetObject2: IChild;
  end;

  TChild = class(TContainedObject, IChild)
    //reference from the child to the parent, always [weak] if circular.
    FObj1: TParent;   //not an interface will not get refcounted.  
    constructor Create(const aObj1: IParent);
    destructor Destroy; override;
  end;

  constructor TChild.Create(const aObj1: IParent);
  begin
    inherited Create;
    FObject1:= (aObj1 as TParent);
  end;

 destructor TParent.Destroy;
 begin
   if Assigned(FChild) then FChild.InvalidateYourPointersToParent(self);
   inherited;
 end;

Это также обеспечит правильное расположение интерфейсов, однако теперь TChild.FObject1 не будет автоматически получить нуль. Возможно, вы сможете поместить код в деструктор TParent, чтобы просмотреть все его дочерние элементы и сообщить их как в показанном коде. Если один из участников циркулярной ссылки не может сообщить о своих слабосвязанных аналогах, вам нужно настроить другой механизм, чтобы снизить эти слабые ссылки.

1
ответ дан Johan 25 August 2018 в 21:57
поделиться

Похоже, вы хотите, чтобы оба объекта делили счетчик ссылок. Вы можете сделать это, разрешив третьему объекту (TPair) обрабатывать подсчет ссылок. Хорошим способом добиться этого является использование ключевого слова implements. Вы можете сохранить этот третий объект скрытым или взаимодействовать с ним.

С помощью приведенного ниже кода вы можете создать TPairChildA, TPairChildB или их «родительский» TPair , Любой из них будет создавать остальные, когда это необходимо, и все созданные объекты будут сохранены до тех пор, пока не будут указаны ссылки. Вы можете, конечно, добавить к объектам такие интерфейсы, как ваш IObject1, но я сохранил их для простоты.

unit ObjectPair;

interface

type
  TPairChildA = class;
  TPairChildB = class;

  TPair = class( TInterfacedObject )
  protected
    FChildA : TPairChildA;
    FChildB : TPairChildB;

    function GetChildA : TPairChildA;
    function GetChildB : TPairChildB;
  public
    destructor Destroy; override;

    property ChildA : TPairChildA read GetChildA;
    property ChildB : TPairChildB read GetChildB;
  end;

  TPairChild = class( TObject , IInterface )
  protected
    FPair : TPair;

    property Pair : TPair read FPair implements IInterface;
  public
    constructor Create( APair : TPair = nil ); virtual;
  end;

  TPairChildA = class( TPairChild )
  protected
    function GetSibling : TPairChildB;
  public
    constructor Create( APair : TPair = nil ); override;

    property Sibling : TPairChildB read GetSibling;
  end;

  TPairChildB = class( TPairChild )
  protected
    function GetSibling : TPairChildA;
  public
    constructor Create( APair : TPair = nil ); override;

    property Sibling : TPairChildA read GetSibling;
  end;

implementation

//==============================================================================
// TPair

destructor TPair.Destroy;
begin
  FChildA.Free;
  FChildB.Free;
  inherited;
end;

function TPair.GetChildA : TPairChildA;
begin
  if FChildA = nil then
    FChildA := TPairChildA.Create( Self );
  Result := FChildA;
end;

function TPair.GetChildB : TPairChildB;
begin
  if FChildB = nil then
    FChildB := TPairChildB.Create( Self );
  Result := FChildB;
end;

// END TPair
//==============================================================================
// TPairChild

constructor TPairChild.Create( APair : TPair = nil );
begin
  if APair = nil then
    FPair := TPair.Create
  else
    FPair := APair;
end;

// END TPairChild
//==============================================================================
// TPairChildA

constructor TPairChildA.Create( APair : TPair = nil );
begin
  inherited;
  FPair.FChildA := Self;
end;

function TPairChildA.GetSibling : TPairChildB;
begin
  Result := FPair.ChildB;
end;

// END TPairChildA
//==============================================================================
// TPairChildB

constructor TPairChildB.Create( APair : TPair = nil );
begin
  inherited;
  FPair.FChildB := Self;
end;

function TPairChildB.GetSibling : TPairChildA;
begin
  Result := FPair.ChildA;
end;

// END TPairChildB
//==============================================================================

end.

Пример использования:

procedure TForm1.Button1Click( Sender : TObject );
var
  objA : TPairChildA;
  ifA , ifB : IInterface;
begin
  objA := TPairChildA.Create;
  ifA := objA;
  ifB := objA.Sibling;
  ifA := nil;
  ifB := nil; // This frees all three objects.
end;
0
ответ дан Marthein 25 August 2018 в 21:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: