我将AVQueuePlayer子类化,并在我的构造函数中传递需要播放的AVPlayerItem的位置,我想在要播放的第一个项目上添加观察者。
所以我正在使用AVPlayer方法addBoundaryTimeObserverForTimes:queue:usingBlock:
。正确的实现要求我在addBoundary方法返回的“不透明”对象上调用removeTimeObserver:
。
为了将对象保留很长时间,我将其声明为__block ivar:
@property (nonatomic, copy) __block id obs;
然后在我自己的init方法中,我有:
__block AVPlayer* blockPlayer = self;
_obs = [self addBoundaryTimeObserverForTimes: times
queue:NULL
usingBlock:^{
// Post a notification that I can then act on
[[NSNotificationCenter defaultCenter]
postNotificationName:@"PlaybackStartedNotification"
object:nil];
// Remove the boundary time observer
[blockPlayer removeTimeObserver:_obs]; // Warning here
}];
这里发生了很多事情……尽管当我尝试删除时间观察者时出现了特定的警告,但我也在发布通知,我也可以更改该通知以在object:part中传递变量。我也将自己设置为观察者...
我已经阅读了许多其他有关潜在解决方案的答案(example)
但是我还没有发现有关使用块变量的任何信息。
我的代码不安全还是可以吗?
编辑:我最初将@property的名称误输入为
__block id observer
,而实际上的确是__block id obs
。因此,接受的答案可以回答这两种情况! (太棒了!)
最佳答案
(所有直接输入答案的代码,都将其视为伪代码,并期望至少有少量错字!)
不幸的是,您误解了__block
的目的和行为-这是一个仅适用于局部变量并修改其生存期的属性,因此可以通过块安全地对其进行更新。所以:
为了将对象保留很长时间,我将其声明为__block ivar:
@property (nonatomic, copy) __block id observer;
是无效的属性声明,因为属性是通常由实例变量而非本地变量支持的实例方法。不幸的是,当前的Apple编译器只是忽略了无意义的
__block
而不是报告错误-以前引起SO inquirers混乱的错误(您应该向Apple提交错误报告,以鼓励他们对其进行修复)。接下来,您编写:
__block AVPlayer* blockPlayer = self;
因此,您可以在块中使用
blockPlayer
而不是self
,以尝试避免保留周期。这样做不是而不是工作,的确确实简单地在循环中添加了另一个(匿名)对象。__weak AVPlayer *blockPlayer = self;
弱引用会破坏一个循环,但是您必须首先在该块中创建一个强引用,然后检查它是否不是NULL
-弱引用的对象将被销毁。在您的代码块中执行此操作的代码将类似于:// Remove the boundary time observer if blockPlayer still exists
AVPlayer *strongBlockPlayer = blockPlayer; // obtain strong reference from weak one
if (strongBlockPlayer)
[strongBlockPlayer ...];
即使进行了这些更改,您仍然面临更大的问题。在您的代码中,您有:_obs = [self addBoundaryTimeObserverForTimes:times
queue:NULL
usingBlock:^{
...
[blockPlayer removeTimeObserver:_obs];
}];
在这里,您尝试在块中使用
_obs
的值。此时,您的问题还不清楚,
_obs
是要作为局部变量还是observer
属性?我们将考虑两种情况:局部变量
如果
_obs
是局部变量,则您的代码将无效。创建一个块时,该块使用的任何局部变量的值都会复制到该块本身中,因此此处将复制_obs
的值。但是_obs
此时将没有有效值,只有在对addBoundaryTimeObserverForTimes:queue:usingBlock:
的调用返回并分配了其返回值之后(即在创建块并将其传递给同一调用之后),才发生有效值。此问题类似于defining a local recursive block,并且具有相同的解决方案。如果使用
__block
属性声明了局部变量,因此对其生命周期进行了修改以匹配使用该变量的任何块的寿命,从而这些块可以修改其值,则该局部变量中的值为而不是将复制到该块中-取而代之的是,该块获取对该变量的引用,该引用用于根据需要读取/写入该变量。因此,为使代码正常工作,请将其更改为:
__block id obs = [self addBoundaryTimeObserverForTimes:times
queue:NULL
usingBlock:^{
...
[blockPlayer removeTimeObserver:obs];
}];
在此版本中:obs
,使其寿命至少与您的块一样长(我们将跳过有关编译器如何安排它的详细信息-它们很有趣,但并不是至关重要的); jobs
创建的; addBoundaryTimeObserverForTimes:queue:usingBlock:
方法将其传递给块; obs
;和obs
,并在方法调用后获取存储在其中的值。 物业参考
如果您输入错误,并且希望将
_obs
设置为observer
属性,则分配的LHS应该是self.observer
,而RHS应该是blockPlayer.observer
,这允许需要使用弱引用:__weak AVPlayer *blockPlayer = self;
self.observer = [self addBoundaryTimeObserverForTimes:times
queue:NULL
usingBlock:^{
...
// Remove the boundary time observer if blockPlayer still exists
AVPlayer *strongBlockPlayer = blockPlayer; // obtain strong reference from weak one
if (strongBlockPlayer)
[strongBlockPlayer removeTimeObserver:strongBlockPlayer.observer];
}];
就像在调用该块并读取strongBlockPlayer.observer
时一样,将返回对addBoundaryTimeObserverForTimes:queue:usingBlock:
块的调用,并进行属性分配。/
obs
的局部变量与属性? 以上两个版本中的哪一个(哪个都应该工作)更好?可能是局部变量版本为(a)您似乎在其他地方不需要该属性,并且(b)将变量的需求本地化为仅语句,方法调用(需要它),这反过来有助于提高可读性和调试。但是,这是一种意见,有些人可能会不同意-自行选择!
高温超导
关于ios - 保留有关ivar的__block变量的循环警告,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32032789/