我寫了一個UIView子類「VideoPlayerView」來封裝AVFoundation視頻播放。我相信我有一個防彈KVO模式,用於處理觀察AVPlayer,AVPlayerItems和AVURLAssets以加載,回放和錯誤處理。視頻播放器的AVFoundation KVO模式有什麼問題[ref:AVPlayerLayer,AVPlayerItem,AVURLAsset]?
相反,我發現崩潰被報道,這種模式是專門設立的防範(很少,但仍然報道)。
a)類AVPlayerItem的實例0x170019730被解除分配,而鍵值觀察者仍然在其中註冊。
二)[VideoPlayerView setPlayerItem:]不能從AVPlayerItem關鍵路徑「狀態」,因爲它未註冊爲觀察員刪除觀察者VideoPlayerView。
C)[VideoPlayerView setAsset:]無法從AVURLAsset 0x170233780關鍵路徑 「可玩」,因爲它沒有被註冊爲觀察者除去觀察者VideoPlayerView 0x145e3bbd0。
我想了解爲什麼會出現這些錯誤,我錯過了什麼,是怎麼讓事情變得更強健。
爲了解釋的目的,對具體的細節進行了簡化,但我相信所有相關信息都在這裏。
我有一個類VideoPlayerView,它擁有這些特性在其他之中:
@property (strong, nonatomic) AVPlayerItem *playerItem;
@property (strong, nonatomic) AVURLAsset *asset;
@property (strong, nonatomic, readonly) AVPlayerLayer *playerLayer;
需要注意的是,指的都是強 - 這些對象不能被釋放,直到VideoPlayerView(這是做觀察)本身釋放。 AVPlayerLayer maintains a strong reference to its AVPlayer property。
我實現定製的吸氣劑如下:
- (AVPlayer*)player
{
return [(AVPlayerLayer*)self.layer player];
}
- (AVPlayerLayer *)playerLayer
{
return (AVPlayerLayer *)self.layer;
}
我實現自定義設置器如下:
- (void) setPlayer:(AVPlayer*)player
{
// Remove observation for any existing player
AVPlayer *oldPlayer = [self player];
[oldPlayer removeObserver:self forKeyPath:kStatus];
[oldPlayer removeObserver:self forKeyPath:kCurrentItem];
// Set strong player reference
[(AVPlayerLayer*)[self layer] setPlayer:player];
// Add observation for new player
[player addObserver:self forKeyPath:kStatus options:NSKeyValueObservingOptionNew context:kVideoPlayerViewKVOContext];
[player addObserver:self forKeyPath:kCurrentItem options:NSKeyValueObservingOptionNew context:kVideoPlayerViewKVOContext];
}
- (void) setAsset:(AVURLAsset *)asset
{
// Remove observation for any existing asset
[_asset removeObserver:self forKeyPath:kPlayable];
// Set strong asset reference
_asset = asset;
// Add observation for new asset
[_asset addObserver:self forKeyPath:kPlayable options:NSKeyValueObservingOptionNew context:kVideoPlayerViewKVOContext];
}
- (void) setPlayerItem:(AVPlayerItem *)playerItem
{
// Remove observation for any existing item
[_playerItem removeObserver:self forKeyPath:kStatus];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
[nc removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:_playerItem];
[nc removeObserver:self name:AVPlayerItemFailedToPlayToEndTimeNotification object:_playerItem];
// Set strong playerItem reference
_playerItem = playerItem;
// Add observation for new item
[_playerItem addObserver:self forKeyPath:kStatus options:NSKeyValueObservingOptionNew context:kVideoPlayerViewKVOContext];
if (_playerItem)
{
[nc addObserver:self selector:@selector(handlePlayerItemDidReachEndTimeNotification:) name:AVPlayerItemDidPlayToEndTimeNotification object:_playerItem];
[nc addObserver:self selector:@selector(handlePlayerItemFailureNotification:) name:AVPlayerItemPlaybackStalledNotification object:_playerItem];
[nc addObserver:self selector:@selector(handlePlayerItemFailureNotification:) name:AVPlayerItemFailedToPlayToEndTimeNotification object:_playerItem];
}
}
外這些自定義設置器的,VideoPlayerView始終使用 「self.property =」 或「[ self setProperty:]「並且從不」_property =「,這樣自定義設置器總是被使用。
最後,VideoPlayerView實現dealloc方法如下:
- (void) dealloc
{
[self releasePlayerAndAssets];
}
- (void) releasePlayerAndAssets
{
[self setAsset:nil];
[self setPlayerItem:nil];
[self setPlayer:nil];
}
是的,我應該只是內聯這個毫無意義的抽象!儘管如此,這意味着在重新分配VideoPlayerView時,其中的任何強大屬性都會將其觀察刪除,然後再釋放以允許其重新分配。
那麼,我相信這個模式應該減輕我觀察崩潰如下:
一)一個實例類的0x170019730 AVPlayerItem被釋放,而鍵值觀察家仍用它註冊。
VideoPlayerView是我觀察AVPlayerItem的唯一類。 VideoPlayerView在觀察它時保持對AVPlayerItem的強引用。因此,AVPlayerItem無法在VideoPlayerView處於活動狀態時解除分配,並且在釋放AVPlayerItem之前,VideoPlayerView將在AVPlayerItem的後續釋放之前停止觀察AVPlayerItem。
這是怎麼回事?
二)[VideoPlayerView setPlayerItem:]不能從AVPlayerItem關鍵路徑「狀態」,因爲它未註冊爲觀察員刪除觀察者VideoPlayerView。
C)[VideoPlayerView setAsset:]無法從AVURLAsset 0x170233780關鍵路徑 「可玩」,因爲它沒有被註冊爲觀察者除去觀察者VideoPlayerView 0x145e3bbd0。
我的自定義setter正試圖刪除任何先前設置的AVPlayerItem或AVURLAsset的觀察,然後用指向新的或傳入的AVPlayerItem或AVURLAsset的指針替換該屬性。
當我的類被實例化時,_playerItem和_asset都是零。因此,任何以前的AVPlayerItem或AVURLAsset都必須通過自定義設置器進行設置,因此已將VideoPlayerView註冊爲這些關鍵路徑的觀察者。
這些屬性如何在未設置觀察的情況下設置?
是基於自定義設置器方法調用的順序對這些太可怕競爭條件?
這裏有什麼基本的東西嗎?
我正在考慮使用objective-c運行時在這些對象上創建關聯的對象屬性BOOL isObserved,以便能夠在嘗試刪除觀察者之前執行完整性檢查。我感覺即使這樣做不夠健壯,考慮到目前的方法論的問題。
任何見解或幫助非常感謝。謝謝你的閱讀。
與崩潰無關,但在'setPlayerItem'中,您從新參數'playerItem'中刪除通知觀察者,而不是舊ivar'_playerItem'。 – Willeke
謝謝您指出@Willeke - 非常感謝。 –