(所有直接输入答案的代码,将其视为伪代码并期望至少有轻微的拼写错误!)
不幸的是,您误解了__block 的目的和行为 - 这是一个仅适用于局部变量并修改其生命周期的属性,以便可以安全地更新它们一个街区。所以:
为了将对象保留多长时间,我将其声明为 __block ivar:
@property (nonatomic, copy) __block id observer;
是一个无效的属性声明,因为属性是 instance 方法,通常由 instance - 而不是 local - 变量支持。不幸的是,当前的 Apple 编译器只是忽略了无意义的 __block,而不是报告错误 - 以前曾导致 SO inquirers 混淆的错误(您应该向 Apple 提交错误报告以鼓励他们修复它)。
接下来你写:
__block AVPlayer* blockPlayer = self;
因此您可以在您的块中使用blockPlayer 而不是self 以避免保留循环。这确实不工作,实际上它只是简单地将另一个(匿名)对象添加到循环中......你需要的是一个 weak 引用:
__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/observer 的局部变量与属性?
以上两个版本,哪一个更好?可能是局部变量版本(a)您似乎不需要其他地方的属性,并且(b)它将对变量的需求本地化为语句,方法调用,它需要它,这反过来有助于可读性和调试. 然而这是一个意见,有些人可能不同意 - 做出你自己的选择!
HTH