Как правильно обращаться с освобожденным делегатом из очереди nsoperation - PullRequest
11 голосов
/ 06 мая 2011

В моем текущем проекте несколько контроллеров представления (например, vc) порождают объекты NSOperation (например, operation), которые выполняются в статическом NSOperationQueue. Пока операция ожидает или выполняется, она сообщит контроллеру представления через делегирование (operation.delegate = vc, назначено, не сохранено).

Однако эти операции могут занять некоторое время, и в то же время приложение может освободить контроллер представления (извлекая их из стека контроллера навигации).

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

Теперь это также вызывает проблему. После освобождения контроллера представления любые вызовы активному делегату NSOperation приведет к неправильному нарушению доступа. Из того, что я понимаю, невозможно проверить, был ли освобожден объект в указателе , как указано в этом вопросе .

Единственное исправление, о котором я могу подумать, - это сохранить операцию и установить для operation.delegate значение nil на dealloc. Но это было бы мое наименее популярное исправление, поскольку в нем было бы много дополнительных свойств / ivars, которые нужно отслеживать.

Поэтому у меня вопрос: есть ли другие способы обойти эту проблему, и если да, не могли бы вы набросать один здесь?

Приветствия
EP.

РЕШЕНИЕ : Подход, который сработал лучше всего для меня, немного отличался от ответа Гильяно:

  • Реализация каждого протокола делегата в администраторе очередей невозможна (более 20 различных протоколов и более 50 методов), поэтому я сохранил прямые назначения делегатов. Что я сделал, так это изменил класс, делающий вызов присваивания. Раньше это был класс (и делегат), который создал запрос, но теперь он выгружается в администратор очередей.

  • Администратор очередей, рядом с назначением делегата для операции, также содержит вторичный изменяемый словарь для отслеживания пар делегат / операция.

  • Каждый экземпляр делегата вызывает метод [QueueManager invalidateDelegate:self] для освобождения, который затем ищет запрос, принадлежащий делегату, и обнуляет его. Пара словарь операция / делегат затем также удаляется, чтобы обеспечить правильное освобождение операции.

  • Наконец, когда KVO наблюдает за свойством isFinished каждой операции, изменяемый диктет сохраняется чистым, чтобы гарантировать, что все операции сохраняют количество, фактически освобождающееся после их завершения.

Спасибо Гильяно за подсказку по использованию KVO для взлома этого!

Ответы [ 3 ]

7 голосов
/ 06 мая 2011

Я бы предложил пересмотреть вашу архитектуру и переместить делегата в класс (предположим, QueueManager), который управляет очередью, вместо того, чтобы иметь делегата в каждой операции:

  • Создать QueueManagerDelegate, которыйреализует метод (ы), который вам нужен, чтобы уведомить viewControllers

  • В QueueManager добавить наблюдателя KVO для свойства isFinished каждой операции NSOperation (сделайте это перед добавлением операции в очередь;))

  • При обратном вызове KVO вызовите метод (ы) делегата, который вам нужен, только если делегат равен! = Nil

  • Добавьте недействительный методв QueueManager и вызовите этот метод в методе dealloc вашего UIViewController (s)

    - (void) invalidate {self-> Delegate = nil;}

на случай необходимости обновления KVO: Руководство по программированию Kvo

0 голосов
/ 06 мая 2011

Вы также должны проверить, чтобы все делегаты, если не ноль, также могли ответить на сообщение о завершении операции.Вы делаете это с помощью функции respondsToSelector, которой обладают все подклассы NSObject.

В моих проектах я абстрагировал эту проверку в категорию NSObject, которая позволяет мне безопасно вызывать делегатов с произвольным числом аргументов объекта:

- (void) dispatchSelector:(SEL)selector target:(id)target objects:(NSArray*)objects onMainThread:(BOOL)onMainThread {</p> <p>if(target && [target respondsToSelector:selector]) { // Do your delegate calls here as you please } }

Полный пример вы можете увидеть здесь: https://github.com/chaione/ChaiOneUtils/blob/master/Categories/NSObject-Dispatch.m

0 голосов
/ 06 мая 2011

Лучший совет здесь - пересмотреть архитектуру приложения, чтобы избежать подобных ситуаций. Однако, если текущий код не может быть изменен, почему-то, вы можете использовать NSNotificationCenter . Каждый раз, когда ваш контроллер представления освобождается, вы можете опубликовать уведомление, это уведомление должно быть перехвачено владельцем NSOperationQueue , простой цикл foreach в обработчике уведомлений для nil делегата для освобожденного представления контроллер. Должен сделать трюк.

...