这是Delphi媒体播放器的代码:
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;
答案 0 :(得分:2)
TNSUrl.OCClass.URLWithString
和TAVPlayerItem.OCClass.playerItemWithURL
将项目添加到自动释放池中。因此,保留计数为1.当autorelease池释放它包含的项时,它们将被释放,这通常在当前事件完成执行后发生。
因此需要FPlayerItem.retain
,因为在函数退出后不应释放FPlayerItem。它已分配给FPlayerItem
,因此您希望将其保持活动状态。
通常,如果您使用Create
,alloc
,copy
,mutableCopy
,new...
创建此类,则会为您调用retain。然后,您需要致电release
或autorelease
。
如果使用其他函数(如fileUrlWithPath
)创建此类,则会将其添加到自动释放池中。您的保留计数仍然为1,但它将为您释放。如果你也发布它,那么你会崩溃。
如果您在课堂上致电retain
,那么该通话必须与release
平衡。
答案 1 :(得分:1)
FPlayer
和FPlayerItem
是围绕Objective-C原始对象指针的Delphi对象包装器。
虽然Delphi for iOS和底层iOS框架都使用引用计数来管理对象实例的生命周期,但所有相似之处都在那里结束。这是两个独立的引用计数机制。
虽然保持对FPlayer
和FPlayerItem
的强引用可确保Delphi包装器实例的生命周期,但调用retain
会增加包装的Objective-C对象实例的引用计数,并在此期间保持该对象实例的活动状态包装器本身的生命周期。
不调用retain
包装对象可能会被操作系统释放,而Delphi包装器仍然使用它。
当然,要减少包装对象的引用计数,必须在包装器被销毁时使用匹配的release
调用。
至于在FPlayerItem.release;
期间发生异常的原因很难说清楚。它可能是线程问题,FMX部分中的错误甚至是底层操作系统框架。
就包装的Objective-C实例而言,它们在需要的地方保留了自己的强引用,因此就它们而言,释放顺序并不重要,并且OS也不太可能是这里的罪魁祸首(我不能肯定地说)。
但如果我必须编写上面的析构函数代码,我会使用以下模式来避免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;