删除 NSOperation 的观察者
我有一个通过 NSOperationQueue 中的 NSOperation 加载数据的视图。我想允许用户在操作完成之前离开此视图。我的问题是我似乎无法始终如一地做到这一点而不崩溃。这是我开始操作的代码:
NSOperationQueue* tmpQueue = [[NSOperationQueue alloc] init];
self.queue = tmpQueue;
[tmpQueue release];
SportsLoadOperation* loadOperation = [[SportsLoadOperation alloc] init];
[loadOperation addObserver:self forKeyPath:@"isFinished" options:0 context:NULL];
[self.queue addOperation:loadOperation];
[loadOperation release];
如果我在操作仍在执行时离开视图,我经常会收到此错误:
[SportsViewController retain]: message sent to deallocated instance 0x38b5a0
如果我尝试删除观察者以免发生这种情况,如下所示:
-(void)viewWillDisappear:(BOOL)animated {
if (self.isLoadingData) {
for (NSOperation *operation in [self.queue operations]) {
if([operation isExecuting]) {
[operation cancel];
[operation removeObserver:self forKeyPath:@"isFinished"];
}
}
}
[super viewWillDisappear:animated];
}
然后我有时会收到此错误错误:
Terminating app due to uncaught exception 'NSRangeException', reason:
'Cannot remove an observer <SportsViewController 0x661c730> for the key path "isFinished" from <SportsLoadOperation 0x66201a0> because it is not registered as an observer.'
如何避免这些问题?
I have a view which loads data via an NSOperation within an NSOperationQueue. I want to allow users to leave this view before the operation has completed. My problem is that I can't seem to consistently do this without crashing. Here is my code to start the operation:
NSOperationQueue* tmpQueue = [[NSOperationQueue alloc] init];
self.queue = tmpQueue;
[tmpQueue release];
SportsLoadOperation* loadOperation = [[SportsLoadOperation alloc] init];
[loadOperation addObserver:self forKeyPath:@"isFinished" options:0 context:NULL];
[self.queue addOperation:loadOperation];
[loadOperation release];
If I leave the view while the operation is still executing, I often get this error:
[SportsViewController retain]: message sent to deallocated instance 0x38b5a0
If I try to remove the observers so that this doesn't occur, like this:
-(void)viewWillDisappear:(BOOL)animated {
if (self.isLoadingData) {
for (NSOperation *operation in [self.queue operations]) {
if([operation isExecuting]) {
[operation cancel];
[operation removeObserver:self forKeyPath:@"isFinished"];
}
}
}
[super viewWillDisappear:animated];
}
Then I sometimes get this error:
Terminating app due to uncaught exception 'NSRangeException', reason:
'Cannot remove an observer <SportsViewController 0x661c730> for the key path "isFinished" from <SportsLoadOperation 0x66201a0> because it is not registered as an observer.'
How can I avoid these problems?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
第二条错误消息说明了一切。
您是否尝试过在
[操作取消]
之后不removeObserver
并看看会发生什么?您是否尝试过先
removeObserver
,然后才取消
操作?这些可能有助于缩小触发错误的条件范围。此外,您可能希望将日志输出添加到代码中以查看其实际执行时间。
而且,就像自由空间的答案所说,添加 &删除观察者最好在观察实例的构造/销毁方法中完成。这通常会产生更稳定的代码。
The 2nd error message says it all.
Have you tried to not
removeObserver
after[operation cancel]
and see what happens then?Have you tried to first
removeObserver
and only thencancel
the operation?These might help to narrow down the conditions that trigger the error. Also, you might want to add log output to the code to see when it actually executes.
And, like freespace's answer says, adding & removing observers is best done in the construction / destruction methods of the observed instances. This generally yields more stable code.
看起来您有一个
SportsLoadOperation
实例,但没有SportsViewController
作为观察者。您是否在代码中的其他位置插入了SportsLoadOperation
?如果是这种情况,请考虑为SportsLoadOperaion
编写一个initWithObserver
方法来自动进行观察。这可以避免由于忘记在isFinished
上设置观察者而导致的错误。另外,最好在dealloc中然后在viewWillDisappear中删除观察者,因为在许多情况下都会调用viewWillDisappear,例如在显示模态视图控制器。因此,您可能会过早停止运营。
编辑
不要检查
[operation isExecuting]
,而是检查[operation isCancelled]
。这是因为[operation cancel]
不会停止操作 - 如果您实际上没有在main< 中检查
isCancelled
,它可以并且将会继续执行。 /代码> 方法。这意味着,如果viewWillDisappear
被调用两次,您最终可能会在同一SportsLoadOperation
实例上尝试调用removeObserver
两次,第二次尝试失败。在以下位置添加一些调试输出语句:
SportsLoadOperation
实例并将其插入队列SportsLoadOperation
实例并从中删除观察者时。Looks like you have an instance of
SportsLoadOperation
that doesn't haveSportsViewController
as an observer. Are you insertingSportsLoadOperation
anywhere else in your code? If this is the case, consider writing aninitWithObserver
method forSportsLoadOperaion
that will do the observing automatically. This avoids errors caused by forgetting to set the observer onisFinished
.Also, it is probably better to do the removal of observer in
dealloc
then inviewWillDisappear
becauseviewWillDisappear
is called in many circumstances, e.g. when displaying a modal view controller. Thus you might be prematurely stopping your operations.Edit
Instead of checking against
[operation isExecuting]
check against[operation isCancelled]
. This is because[operation cancel]
doesn't stop an operation - it can and will continue executing if you don't actually check forisCancelled
in yourmain
method. This means that ifviewWillDisappear
is called twice, you could end up attempting to callremoveObserver
twice on the same instance ofSportsLoadOperation
, with the second attempt failing.Add some debugging output statements in the following places:
SportsLoadOperation
instance and insert it into the queueuSportsLoadOperation
instance and removing from it observers.我最终通过重写观察操作的 addObserver 和 removeObserver 方法来解决这个问题,以跟踪观察者,以便我可以在 [cancel] 方法中删除它们。
我现在要做的就是调用操作队列来取消所有操作,然后再关闭正在观察操作的控制器。
下面,_observers 是一个 NSMutableDictionary。
}
I ended up solving this by overriding the observed operation's addObserver and removeObserver methods, to keep track of observers so I could remove them in the [cancel] method.
All I have to do now is call the operation queue to cancel all operations before I dismiss the controller that was observing the operations.
Below, _observers is an NSMutableDictionary.
}