在块函数中使用 KVO
Using KVO in a block function
有没有什么好的方法可以同时使用key-value-observation和blocks?我有一个接受完成块的函数,当观察到的状态变为 AVPlayerItemStatusReadyToPlay
时,我希望这个完成块为 运行。我能否以某种方式使用观察者的上下文传递块,或者这是否会破坏 KVO 编程的基础?
- (void)setVideoWithURL:(NSURL *)url completed:(PlayerCompletedWithFinishedBlock)completedBlock {
...
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([change isEqual: @"AVPlayerItemStatusReadyToPlay"]) {
// Is there a way to run the completion block from here?
}
}
只需将块存储在 copy
属性 中,然后在 -observeValueForKeyPath:...
方法中调用它。删除观察时不要忘记清除强引用。
您确实应该在添加观察者时为上下文使用唯一值,您需要在 -observeValueForKeyPath:...
中检查它。但是,使用块不是一个好主意。一方面,你仍然需要保持对块的强引用,所以它并不能避免存储这种强引用的需要。
上下文应该是对象的一种方式:a) 确定对 -observeValueForKeyPath:...
的调用对应于该代码自己的观察; b) 以一种将观察与可能由其他代码设置的任何其他观察区分开来的方式删除观察(使用 -removeObserver:forKeyPath:context:
)。因此,上下文应该标识使用它的代码,而不是任何特定的观察者或被观察者。通常的做法是定义一个静态变量并使用它的地址。
最后,即使是您展示的零散的 -observeValueForKeyPath:...
实现也很糟糕。 change
永远不会等于 @"AVPlayerItemStatusReadyToPlay"
因为前者是一个字典而后者是一个字符串。除了检查上下文(如果通知不适合您,则调用 super),您还应该检查 keyPath
或 object
的 status
属性 .
Ken 是对的——您的 KVO 通知处理程序 (-observeValueForKeyPath
) 不会按原样工作。采纳他的建议
但是您还应该将 PlayerItem
作为 属性 存储在当前对象中,并确保您在某处有匹配的 -removeObserver
(可能在 dealloc
, 你可以检查 playerItem
是否存在,如果存在,则 -removeObserver
正如 Ken 所说,您可以为您的 completionBlock
创建一个新的 属性 来存储它;因此稍后可以在您的 KVO 通知处理程序中调用它。
他还提到使用复制,并清除你对你的块的强引用。这是因为如果说例如块来自保留它的同一个 class,则通常会以保留周期结束。如果块中的代码引用了 属性,它将保留拥有 属性 的对象。而同一个对象也在保留块,那么你将以一个保留循环结束。因此,请谨慎对待您的参考资料:)
有没有什么好的方法可以同时使用key-value-observation和blocks?我有一个接受完成块的函数,当观察到的状态变为 AVPlayerItemStatusReadyToPlay
时,我希望这个完成块为 运行。我能否以某种方式使用观察者的上下文传递块,或者这是否会破坏 KVO 编程的基础?
- (void)setVideoWithURL:(NSURL *)url completed:(PlayerCompletedWithFinishedBlock)completedBlock {
...
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:NULL];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([change isEqual: @"AVPlayerItemStatusReadyToPlay"]) {
// Is there a way to run the completion block from here?
}
}
只需将块存储在 copy
属性 中,然后在 -observeValueForKeyPath:...
方法中调用它。删除观察时不要忘记清除强引用。
您确实应该在添加观察者时为上下文使用唯一值,您需要在 -observeValueForKeyPath:...
中检查它。但是,使用块不是一个好主意。一方面,你仍然需要保持对块的强引用,所以它并不能避免存储这种强引用的需要。
上下文应该是对象的一种方式:a) 确定对 -observeValueForKeyPath:...
的调用对应于该代码自己的观察; b) 以一种将观察与可能由其他代码设置的任何其他观察区分开来的方式删除观察(使用 -removeObserver:forKeyPath:context:
)。因此,上下文应该标识使用它的代码,而不是任何特定的观察者或被观察者。通常的做法是定义一个静态变量并使用它的地址。
最后,即使是您展示的零散的 -observeValueForKeyPath:...
实现也很糟糕。 change
永远不会等于 @"AVPlayerItemStatusReadyToPlay"
因为前者是一个字典而后者是一个字符串。除了检查上下文(如果通知不适合您,则调用 super),您还应该检查 keyPath
或 object
的 status
属性 .
Ken 是对的——您的 KVO 通知处理程序 (-observeValueForKeyPath
) 不会按原样工作。采纳他的建议
但是您还应该将 PlayerItem
作为 属性 存储在当前对象中,并确保您在某处有匹配的 -removeObserver
(可能在 dealloc
, 你可以检查 playerItem
是否存在,如果存在,则 -removeObserver
正如 Ken 所说,您可以为您的 completionBlock
创建一个新的 属性 来存储它;因此稍后可以在您的 KVO 通知处理程序中调用它。
他还提到使用复制,并清除你对你的块的强引用。这是因为如果说例如块来自保留它的同一个 class,则通常会以保留周期结束。如果块中的代码引用了 属性,它将保留拥有 属性 的对象。而同一个对象也在保留块,那么你将以一个保留循环结束。因此,请谨慎对待您的参考资料:)