2014-02-14 1 views
3

Я работаю над проектом, где я имею сопрягаемую TRectangle, как это:Избавление сопрягаемого объекта с ARC против без - Delphi

IBoardShape = interface(IInterface) 
    function GetColor: integer; 
    procedure SetColor(const aColor: integer); 
    property Color: integer read GetColor write SetColor; 
end; 

TGameRectangle = class(TRectangle, IBoardShape) 
private 
    FColor: integer; 
    procedure SetColor(const aColor: integer); 
    function GetColor: integer; 
    property Color: integer read GetColor write SetColor; 
protected 
    {$IFNDEF AUTOREFCOUNT} 
    [Volatile] FRefCount: Integer; 
    {$ENDIF} 
    function _AddRef: Integer; stdcall; 
    function _Release: Integer; stdcall; 
end; 

_AddRef и _Release такого же, как в InterfacedObject:

function TGameRectangle._AddRef: Integer; 
begin 
{$IFNDEF AUTOREFCOUNT} 
    Result := AtomicIncrement(FRefCount); 
{$ELSE} 
    Result := __ObjAddRef; 
{$ENDIF} 
end; 

function TGameRectangle._Release: Integer; 
begin 
{$IFNDEF AUTOREFCOUNT} 
    Result := AtomicDecrement(FRefCount); 
    if Result = 0 then 
    Destroy; 
{$ELSE} 
    Result := __ObjRelease; 
{$ENDIF} 
end; 

Чтобы создать прямоугольник, что я делаю это:

var 
    lRect: TGameRectangle; 
begin 
    lRect := TGameRectangle.Create(self); 
    lRect.Parent := Layout1; 
    lRect.Align := TAlignLayout.alClient; 
    FIntObj := lRect as IBoardShape; 

Позже я бесплатно его, установив FIntObj на номер nil. В Windows, когда я следую _Release, счетчик ссылок равен 1, и счетчик уменьшается и объект освобождается. При работе в Android количество ссылок составляет 5, когда я вхожу в _Release (счетчик ссылок отображается внутри __ObjRelease). Поскольку счетчик ссылок по-прежнему высок, объект не освобождается.

Я воссоздал это в простой демонстрации, используя в основном только код, который я разместил здесь. Может ли кто-нибудь объяснить, что отличается в ARC, что приводит к тому, что счетчик ссылок настолько высок?

+0

При работе с ARC ** каждая ** ссылка на экземпляр увеличивает RefCount. Вы передаете Владельца и родителя, и оба они держат ** ** ** ссылку на этот экземпляр. Кроме того, ссылка на интерфейс у вас минимум 3 - но есть, конечно, больше ссылок, вызванных владельцем и родителем. –

+2

@SirRufo ... если вы не используете ссылку '[weak]', которая необходима, например. для круговых ссылок. См. Статью [Марко] (http://blog.marcocantu.com/blog/automatic_reference_counting_for_delphi.html). ИМХО такое нарушение изменений модели памяти Delphi было одной из худших идей когда-либо. –

+0

@ArnaudBouchez Delphi слишком боится изменений, особенно нарушая изменения. Я стремлюсь к появлению ARC для настольного компилятора. Нет ничего плохого в использовании атрибута '[слабый]'. –

ответ

1

Все это просто не нужно в ARC, потому что ARC уже подсчитывает ссылки и управляет пожизненным временем.

Под ARC вы можете и должны полагаться на реализацию IInterface из базового класса.

Под ARC ваш код должен выглядеть следующим образом:

TGameRectangle = class(TRectangle, IBoardShape) 
private 
    FColor: integer; 
    procedure SetColor(const aColor: integer); 
    function GetColor: integer; 
    property Color: integer read GetColor write SetColor; 
end; 

Ваша большая проблема в том, что ваш код не может работать на не-ARC платформ. Это потому, что у вас есть принадлежащий TComponent потомок. Таким образом, владелец имеет ссылку на объект прямоугольника и будет пытаться уничтожить, когда он будет уничтожен. В дополнение к этому, ваш счетчик ссылок на интерфейс также предполагает право собственности. Как правило, объекты должны иметь ровно один владелец. Ваш дизайн дает им два.

Вы должны на платформах, отличных от ARC, управлять временем жизни через подсчет ссылок на интерфейс, если у объекта нет владельца. Я обсудил это более подробно в своем answer с вашим предыдущим вопросом.

+0

Не просто передать 'nil' как' AOwner' для конструктора 'TRectangle' решить это? –

+0

@ Günther the Beautiful Действительно. Но он этого не делает. –

+0

Спасибо за вашу помощь, как в этом ответе, так и в другом. Я пробовал пропустить ноль, но я получил исключение, как только был выведен метод, создавший объект (внутри _IntfCopy в системе). Я не думаю, что я попытаюсь это сделать (например, вы сказали, что это похоже на то, чтобы идти против дизайна, используя мой собственный), я просто буду отслеживать объект. – Sentient