Uitableview 类AVPlayerItem已解除分配,而键值观察员仍在注册
我得到了以下错误,但奇怪的是,只有当我使用分页在tableview上滚动,加载第二组帖子,然后尝试移动到新的view控制器时,才会发生这种情况, 如果我只加载文章的第一页,并移到另一个视图控制器,则不会发生崩溃:Uitableview 类AVPlayerItem已解除分配,而键值观察员仍在注册,uitableview,pagination,avplayeritem,Uitableview,Pagination,Avplayeritem,我得到了以下错误,但奇怪的是,只有当我使用分页在tableview上滚动,加载第二组帖子,然后尝试移动到新的view控制器时,才会发生这种情况, 如果我只加载文章的第一页,并移到另一个视图控制器,则不会发生崩溃: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x170212330 of class AVPlayerItem was deall
Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'An instance 0x170212330 of class AVPlayerItem was deallocated while key
value observers were still registered with it.
Current observation info: <NSKeyValueObservationInfo 0x174820360> (
<NSKeyValueObservance 0x174244290: Observer: 0x101c76c00, Key path:
playbackBufferEmpty, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243ea0>
<NSKeyValueObservance 0x1754583c0: Observer: 0x101c76c00, Key path:
playbackLikelyToKeepUp, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0,
Property: 0x174243f60>
我从添加第二页中看到的唯一区别是:
执行常规表格视图加载时,我执行以下操作:
[_tableView reloadData];
在同一表视图上加载第二页时,我会:
[_tableView beginUpdates];
[_tableView insertRowsAtIndexPaths:[[GlobalSingleton sharedInstance] indexPathsToInsert] withRowAnimation:UITableViewRowAnimationTop];
[_tableView endUpdates];
在我的手机上,我会:
- (void)prepareForReuse{
[self removePlayer];
}
- (void) dealloc {
[self removePlayer];
}
-(void)removePlayer{
@try{
[self.videoItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
NSLog(@"remove Observer!");
// [avLayer.player pause];
}
@catch (NSException * e) {
NSLog(@"Exception: %@", e);
}
@finally {
NSLog(@"finally");
[self.avLayer removeFromSuperlayer];
self.playIV.hidden = YES;
self.videoActivity.hidden = YES;
self.videoView.hidden = YES;
self.videoItem = nil;
self.avLayer = nil;
self.videoPlayer = nil;
}
}
insertRowsAtIndexPaths是否可能导致细胞永远无法释放?我找不到如何注册观察者。在使用insertRowsAtIndexPaths时,prepareForReuse没有被调用,因此我解决了这个问题,在向单元格添加新的视频项之前,先在CellForRowsatindexPath上注销观察者:
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = @"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSString* videoString = [NSString stringWithFormat:@"%@%@%@",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
/** SOLUTION STARTS HERE **/
@try{
[self.videoItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
@catch (NSException * e) {
NSLog(@"Exception: %@", e);
}
@finally {
NSLog(@"finally");
}
/** SOLUTION ENDS HERE **/
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:@selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}
我的解决方案是删除前两个观察者:[updateCell.videoItem addObserver:updateCell forKeyPath:@“Playbackufferempty”选项:NSKeyValueObservingOptionNew context:nil];[updateCell.videoItem addObserver:updateCell forKeyPath:@“playbackLikelyToKeepUp”选项:NSKeyValueObservingOptionNew context:nil];此外,我还发现prepareForReuse似乎不能很好地处理InsertRowsatindExpath,但如果在创建新的videoItem之前删除CellForRowatineXpath上的观察者,它也可以工作。
- (UITableViewCell *)tableView:(UITableView *)tableViewSelected cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifier = @"postCell";
PostCell* updateCell = [tableViewSelected dequeueReusableCellWithIdentifier:identifier];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
dispatch_sync(dispatch_get_main_queue(), ^{
NSString* videoString = [NSString stringWithFormat:@"%@%@%@",UrlBase,PostVideos,post.video];
NSString* expandedPath = [videoString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *videoURL = [NSURL URLWithString:expandedPath];
/** SOLUTION STARTS HERE **/
@try{
[self.videoItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[self.videoItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
}
@catch (NSException * e) {
NSLog(@"Exception: %@", e);
}
@finally {
NSLog(@"finally");
}
/** SOLUTION ENDS HERE **/
updateCell.videoItem = [AVPlayerItem playerItemWithURL:videoURL];
[updateCell.videoItem addObserver:updateCell forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
[updateCell.videoItem addObserver:updateCell forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
updateCell.videoPlayer = [AVPlayer playerWithPlayerItem:updateCell.videoItem];
updateCell.avLayer = [AVPlayerLayer playerLayerWithPlayer:updateCell.videoPlayer];
updateCell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:@selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:updateCell selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:[updateCell.videoPlayer currentItem]];
updateCell.avLayer.frame = updateCell.picture.bounds;
[updateCell.videoView.layer addSublayer:updateCell.avLayer];
[updateCell.avLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
if(indexPath.row==0){
[updateCell.videoPlayer play];
}
});
});
return updateCell;
}