2017-02-14 19 views
1

Это код медиаплеера Delphi:Зачем нам нужно сохранять для объекта Objective-C?

type 
    TAVMedia = class(TMedia) 
    private 
    FPlayer: AVPlayer; 
    FPlayerItem: AVPlayerItem; 
    public 
    constructor Create(const AFileName: string); override; 
    destructor Destroy; override; 
    end; 

constructor TAVMedia.Create(const AFileName: string); 
var aURL: NSUrl; 
begin 
    inherited Create(AFileName); 
    FPlayerItem := TAVPlayerItem.Wrap(TAVPlayerItem.OCClass.playerItemWithURL(URL)); 
    FPlayerItem.retain; 
    FPlayer := TAVPlayer.Wrap(TAVPlayer.OCClass.playerWithPlayerItem(FPlayerItem)); 
    FPlayer.retain; 
end; 

destructor TAVMedia.Destroy; 
begin 
    FPlayer.release; 
    FPlayer := nil; 
    FPlayerItem.release; 
    FPlayerItem := nil; 
    inherited Destroy; 
end; 

Я не совсем понимаю, почему они должны сделать FPlayerItem.retain и FPlayer.retain? FPlayerItem и FPlayer - это поля объектов, а не локальные переменные, поэтому для них всегда существует сильная ссылка. Итак, какова цель retain?

Кажется, что делает FPlayer.release; также освобождать FPlayerItem, поэтому, когда позже FPlayerItem.release; называют иногда это вызывает нарушение прав доступа (как ни странно, не всегда).

Примечание: я до сих пор не могу понять, почему у меня есть EAccessViolation поэтому я решил поставить здесь полный код именно то, что я сделал:

type 
    TMyMedia = class(TObject) 
    private 
    FPlayer: AVPlayer; 
    FPlayerItem: AVPlayerItem; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    end; 

constructor TMyMedia.Create; 
begin 
    inherited Create; 

    P := TNSUrl.OCClass.URLWithString(StrToNSStr(aDataSource)); // Creates and returns an NSURL object initialized with a provided URL string 
    if P = nil then raise EFileNotFoundException.Create(SFileNotFound); // If the URL string was malformed or nil, returns nil. 
    aURL := TNSUrl.Wrap(P); 
    try 

    FPlayerItem := TAVPlayerItem.Wrap(TAVPlayerItem.OCClass.playerItemWithURL(URL)); 
    FPlayerItem.retain; 

    finally 
    aURL.release; // << if i don't do this then i will not have any exception at the end ??? 
    aURL := nil; // << 
    end; 


    FPlayer := TAVPlayer.Wrap(TAVPlayer.OCClass.playerWithPlayerItem(FPlayerItem)); 
    FPlayer.retain; 
end; 

destructor TAVMedia.Destroy; 
begin 

    ALLog('FPlayer.retainCount', inttostr(FPlayer.retainCount)); // => show 1 
    ALLog('FPlayerItem.retainCount', inttostr(FPlayerItem.retainCount)); // => show 6 

    FPlayer.release; 
    FPlayer := nil; 

    ALLog('FPlayerItem.retainCount', inttostr(FPlayerItem.retainCount)); // => show 1 
    FPlayerItem.release; => here i receive Access violation at address 2156565 accessing address 68684458 

    FPlayerItem := nil; 
    inherited Destroy; 
end; 

ответ

1

FPlayer и FPlayerItem являются Delphi объект обертками вокруг Objective -C указатели необработанных объектов.

Хотя для Delphi для iOS и базовых фреймворков iOS используется подсчет ссылок для управления временем жизни экземпляров объектов, все их сходства заканчиваются. Это два отдельных механизма подсчета ссылок.

При сохранении сильной ссылки на FPlayer и FPlayerItem обеспечивает срок службы экземпляров обертки Delphi, вызывая retain увеличивается счетчик ссылок на завернутый экземпляра объекта Objective-C и сохраняет этот экземпляр объекта в живых в течение срока службы самой обертки.

Без вызова retain обернутый объект может быть освобожден ОС, а оболочка Delphi все еще использует его.

Конечно, чтобы уменьшить количество ссылок на обернутый объект, необходимо использовать соответствующий вызов release, когда оболочка уничтожена.


Как почему исключения бывают во время FPlayerItem.release; это трудно сказать. Это может быть проблема с потоками, ошибка в части FMX или даже базовые ОС.

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

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

destructor TAVMedia.Destroy; 
var 
    tmpPlayer: AVPlayer; 
    tmpPlayerItem: AVPlayerItem; 
begin 
    tmpPlayer := FPlayer; 
    tmpPlayerItem := FPlayerItem; 
    FPlayer := nil; 
    FPlayerItem := nil; 
    tmpPlayer.release; 
    tmpPlayerItem.release; 
    inherited Destroy; 
end; 
+0

Спасибо, Далия, я понимаю, что сейчас очень хорошо нужно позвонить. но это исключение делает меня сумасшедшим. перед тем, как делать FPlayerItem.release, я распечатаю FPlayerItem.retainCount, и это шоу 1. но как только я сделаю FPlayerItem.release, у меня есть eaccessViolation :( –

+0

Есть ли у вас какие-либо обратные вызовы или потоки? Если 'FPlayerItem' установлен на нуль после вас вызовите 'FPlayerItem.release', которые могут использовать объект-оболочку' FPlayerItem', который использует теперь уже не существующий экземпляр Objective-C. –

+0

ничего, я удаляю все (все делегаты и т. д.), чтобы сделать код максимально простым. этот вопрос, чтобы показать вам полный код. Я не могу понять ... –

2

TNSUrl.OCClass.URLWithString и TAVPlayerItem.OCClass.playerItemWithURL добавить элементы в autorelease бассейн. Таким образом, у вас есть счет в 1 раз. Они будут выпущены, когда пул автозапуска освободит содержащиеся в нем элементы, что обычно происходит после завершения текущего события.

Поэтому FPlayerItem.retain необходим, поскольку FPlayerItem не должен быть освобожден после выхода из функции. Он присваивается FPlayerItem, поэтому вы хотите сохранить его в живых.

В общем, если создать такой класс с Create, alloc, copy, mutableCopy, new... затем сохраняют называется для вас. Затем вам необходимо позвонить release или autorelease.

Если вы создаете такой класс с другими функциями, такими как fileUrlWithPath, он добавляется в пул автономии. У вас все еще есть счет на 1, но он будет выпущен для вас. Если вы также отпустите его, вы потерпите крах.

Если вы вызываете retain на класс, то этот вызов должен быть сбалансирован с помощью release.