2017-02-14 6 views
1

Dies ist der Code des Delphi-Media-Player:Warum müssen wir Objective-C-Objektfeld beibehalten?

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; 

Ich verstehe nicht ganz, warum sie FPlayerItem.retain und FPlayer.retain tun müssen? FPlayerItem und FPlayer sind Objektfelder und keine lokalen Variablen, daher gibt es immer einen starken Bezug zu ihnen. Also, was ist der Zweck der retain hier?

Es scheint, dass tun FPlayer.release; wird auch die FPlayerItem freigeben, so wenn später aufgerufen wird manchmal löst es Zugriffsverletzung (seltsamerweise nicht immer).

Hinweis: Ich kann immer noch nicht verstehen, warum ich eine EAccessViolation haben, so habe ich beschlossen, hier den vollständigen Code genau zu setzen, was ich tat:

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; 

Antwort

1

FPlayer und FPlayerItem sind Delphi Objekt Wrapper um das Ziel -C Rohobjektzeiger.

Während sowohl Delphi für iOS als auch die zugrunde liegenden iOS-Frameworks Referenzzählung verwenden, um die Lebensdauer von Objektinstanzen zu verwalten, enden alle Ähnlichkeiten dort. Das sind zwei separate Referenzzählmechanismen.

starke Bezugnahme auf FPlayer und FPlayerItem gewährleistet Lebensdauer der Delphi-Wrapper Fällen ruft retain erhöht Referenzzähler eingewickelt Objective-C Objektinstanz und hält diese Objektinstanz am Leben während der Lebensdauer des Wrapper selbst Während halten.

Ohne Aufruf retain Wrapped-Objekt könnte von OS freigegeben werden, während Delphi Wrapper es immer noch verwendet.

Um die Anzahl der Referenzen auf umgebrochenes Objekt zu verringern, ist es natürlich notwendig, einen passenden release Aufruf zu verwenden, wenn der Wrapper zerstört wird.


Wie, warum Ausnahmen passieren während FPlayerItem.release; ist es schwer zu sagen. Es könnte Threading-Problem, Fehler im FMX-Teil oder sogar zugrunde liegenden OS-Frameworks sein.

Soweit es um Objective-C-Instanzen geht, behalten sie ihre eigenen starken Referenzen, wo sie gebraucht werden, so dass die Reihenfolge nicht wichtig ist, und es ist auch unwahrscheinlich, dass OS der Schuldige ist (ich kann nicht sagen sicher).

Aber wenn ich über Destruktorcode schreiben müsste, würde ich folgendes Muster verwenden, um Probleme auf Delphi Seite zu vermeiden.

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

Dank Dalija, verstehe ich jetzt sehr gut das notwendige Ruf zu behalten. aber diese Ausnahme macht mich verrückt. Kurz bevor ich FPlayerItem.release tue, drucke ich FPlayerItem.retainCount und es wird angezeigt 1. aber sobald ich FPlayerItem.release habe, habe ich einen eAccessViolation :( –

+0

Hast du Callbacks oder Threads? Wenn 'FPlayerItem' nach dir auf Null gesetzt ist Rufen Sie 'FPlayerItem.release' auf, die' FPlayerItem' Wrapper-Objekt verwenden können, das jetzt nicht mehr existierende Objective-C-Instanz verwendet. –

+0

Nichts, ich entferne alles (alle Delegaten, etc), um den Code so einfach wie möglich zu machen Diese Frage, um Ihnen den vollen Code zu zeigen, kann ich nicht verstehen ... –

2

TNSUrl.OCClass.URLWithString und TAVPlayerItem.OCClass.playerItemWithURL die Elemente in den Autofreigabepool hinzufügen. Daher haben Sie eine Retain-Anzahl von 1. Sie werden freigegeben, wenn der Autorelease-Pool die enthaltenen Elemente freigibt, was normalerweise geschieht, nachdem das aktuelle Ereignis beendet wurde.

Daher wird FPlayerItem.retain benötigt, da FPlayerItem nach dem Beenden der Funktion nicht freigegeben werden soll. Es ist zugewiesen, also möchten Sie es am Leben erhalten.

Im Allgemeinen, wenn Sie erstellen eine solche Klasse mit Create, alloc, copy, mutableCopy, new... dann retain für Sie genannt. Dann müssen Sie release oder autorelease anrufen.

Wenn Sie eine solche Klasse mit den anderen Funktionen wie fileUrlWithPath erstellen, wird sie zum Autorelease-Pool hinzugefügt. Sie haben noch eine Retain-Zählung von 1, aber es wird für Sie freigegeben. Wenn Sie es auch freigeben, werden Sie abstürzen. Wenn Sie in einer Klasse retain anrufen, muss dieser Anruf mit einem release ausgeglichen werden.

Verwandte Themen