Замена одинарных кавычек:
function JavaScriptEncode(text){
text = text.replace(/'/g,''')
// More encode here if required
return text;
}
Здесь вы решаете неправильную проблему.
Ваша актуальная проблема заключается не в сильных слабых ссылках, а в том, как можно улучшить ваше решение. Ваша проблема не в , как достичь , но в , чего вы добиваетесь (хотите достичь) .
Чтобы напрямую задать ваши вопросы в первую очередь:
blockquote>
- "TContainedObject (FObj2) .Free;" немного пахнет, но у меня нет лучшего решения, так как мне нужно использовать интерфейс для ссылки на TObject2 (продуктивный код содержит несколько наследований с этой целью). Любые идеи по его очистке?
Здесь вы не можете многое сделать. Вы должны называть
Free
наFObj2
, потому чтоTContainedObject
не управляемый сам класс.blockquote>
- вы легко забываете объявить все ссылки между двумя классами как слабые и ..
Здесь вы ничего не можете сделать. Он поставляется с территорией. Если вы хотите использовать ARC, вам нужно подумать о круговых ссылках.
blockquote>
- аналогичная проблема начинает подниматься с большим количеством классов: с TObject3, на который ссылается одна и ссылается на другую: память протечь. Я мог бы справиться с этим, позволив ему спуститься с TContainedObject, но с устаревшим кодом это может быть непростой задачей.
Вы тоже не можете много сделать. Если ваш дизайн действительно то, что вы хотите иметь, тогда вам просто придется иметь дело со своими сложностями.
Теперь вернемся к тому, почему у вас возникают проблемы в первую очередь.
То, что вы хотите достичь (и вы сделали это с вашим примером кода), поддерживает целостность всей иерархии объектов, захватывая любые ссылки на объекты внутри этой иерархии.
Чтобы перефразировать, вы имеете
Form
иButton
на нем, и вы хотите сохранитьForm
живым, что-то содержитButton
(посколькуButton
сам не будет функционировать). Затем вы хотите добавитьEdit
к этомуForm
и снова сохранить все живое, если что-то схватитEdit
.У вас здесь мало вариантов.
- Держите этот сломанный дизайн и живите с вашим решением, потому что у вас слишком много кода, и изменения будут болезненными. Если вы это сделаете, имейте в виду, что это в конечном счете сломанный дизайн и не пытайтесь повторить его где-либо еще.
- Если у вас есть иерархия, где
TObject1
является корневым классом, который содержит все остальное, то рефакторируйте его и наследоватьTObject2
изTInterfacedObject
, чтобы иметь свой собственный подсчет ссылок и не захватывать ссылки наFObj2
. Вместо этого возьмите экземпляр rootTObject1
и передайте это, если вам действительно нужно.- Это вариация второго подхода. Если
TObject1
не является корневым классом, создайте дополнительный класс-оболочку, содержащий все экземпляры, которые вам нужны, и передайте их.Последние два решения далеки от совершенства, и они не имеют дело с факт, что у вас, вероятно, есть классы, которые слишком много или похожи. Но как бы ни был плохой код, он даже не приближается к вашему текущему решению. И со временем вы можете медленно менять и улучшать эти решения гораздо проще, чем с помощью вашего текущего.
Если вы хотите, чтобы оба объекта были живыми или мертвыми вместе, они, безусловно, являются одним единственным объектом. Хорошо, я понимаю, что оба могут быть разработаны разными людьми, поэтому я бы сделал их обоими членами одного супер-объекта, который подсчитан ссылкой, например
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 в качестве ссылочных объектов, их привязанность друг к другу становится слабой.
Не использовать небезопасные [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
, чтобы просмотреть все его дочерние элементы и сообщить их как в показанном коде. Если один из участников циркулярной ссылки не может сообщить о своих слабосвязанных аналогах, вам нужно настроить другой механизм, чтобы снизить эти слабые ссылки.
Похоже, вы хотите, чтобы оба объекта делили счетчик ссылок. Вы можете сделать это, разрешив третьему объекту (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;