Удаление наблюдателей для NSOperation - PullRequest
1 голос
/ 19 марта 2011

У меня есть представление, которое загружает данные через NSOperation в NSOperationQueue.Я хочу разрешить пользователям выходить из этого представления до завершения операции.Моя проблема в том, что я не могу последовательно делать это без сбоев.Вот мой код для запуска операции:

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.'

Как мне избежать этих проблем?

Ответы [ 3 ]

3 голосов
/ 19 марта 2011

Во втором сообщении об ошибке все сказано.

Вы пытались не removeObserver после [operation cancel] и посмотреть, что произойдет тогда?

Вы пробовалисначала removeObserver и только затем cancel операция?

Это может помочь сузить условия, которые вызывают ошибку.Кроме того, вы можете захотеть добавить вывод журнала в код, чтобы увидеть, когда он действительно выполняется.

И, как говорит ответ freespace, добавление и удаление наблюдателей лучше всего делать в методах построения / уничтожения наблюдаемых экземпляров.Обычно это дает более стабильный код.

0 голосов
/ 15 марта 2012

Я решил эту проблему, переопределив методы addObserver и removeObserver наблюдаемой операции, чтобы отслеживать наблюдателей, чтобы я мог удалить их в методе [cancel].

Все, что мне теперь нужно сделать, это вызватьОчередь операций для отмены всех операций, прежде чем я отклоню контроллер, который наблюдал за операциями.

Ниже _observers является NSMutableDictionary.

- (void)addObserver:(NSObject*)observer
     forKeyPath:(NSString*)keyPath
        options:(NSKeyValueObservingOptions)options context:(void*)context
{
    [super addObserver:observer forKeyPath:keyPath options:options context:context];
    [_observers setValue:observer forKey:keyPath];
}


- (void)removeObserver:(NSObject*)observer forKeyPath:(NSString*)keyPath
{
    [super removeObserver:observer forKeyPath:keyPath];
    [_observers removeObjectForKey:keyPath];
}


- (void)cancel
{
    [super cancel];

    for(id key in _observers)
    {
            id object = [_observers valueForKey:key];
            [super removeObserver:object forKeyPath:key];
    }

    [_observers removeAllObjects];

}

0 голосов
/ 19 марта 2011

Похоже, у вас есть экземпляр SportsLoadOperation, у которого нет SportsViewController в качестве наблюдателя.Вы вставляете SportsLoadOperation где-нибудь еще в вашем коде?Если это так, рассмотрите возможность написания initWithObserver метода для SportsLoadOperaion, который будет выполнять наблюдения автоматически.Это позволяет избежать ошибок, вызванных забыванием установки наблюдателя на isFinished.

. Также, вероятно, лучше удалить наблюдателя в dealloc, чем в viewWillDisappear, потому что viewWillDisappear вызывается во многихобстоятельства, например, при отображении модального контроллера вида.Таким образом, вы можете преждевременно прекратить свои операции.

Изменить

Вместо проверки по [operation isExecuting] проверки по [operation isCancelled].Это потому, что [operation cancel] не останавливает операцию - она ​​может и будет продолжать выполняться, если вы на самом деле не проверяете isCancelled в вашем main методе.Это означает, что если viewWillDisappear вызывается дважды, вы можете в конечном итоге попытаться вызвать removeObserver дважды на одном и том же экземпляре SportsLoadOperation, а вторая попытка не удалась.

Добавить несколько отладочных выходных операторов вследующие места:

  1. при создании экземпляра SportsLoadOperation и вставке его в очередь
  2. при отмене экземпляра SportsLoadOperation и удалении из него наблюдателей.
...