Как правильно обращаться с освобожденным делегатом от очереди

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

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

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

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

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

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

Cheers,
ЕР.

РЕШЕНИЕ. Самый подходящий для меня подход - небольшая вариация ответа Гильяно:

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

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

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

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

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

Ответ 1

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

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

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

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

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

    - (Недействительными) Invalidate {     self- > delegate = nil; }

если вам нужно обновить KVO: руководство по программированию Kvo

Ответ 2

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

Ответ 3

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

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

- (void) dispatchSelector:(SEL)selector target:(id)target objects:(NSArray*)objects onMainThread:(BOOL)onMainThread {

if(target && [target respondsToSelector:selector]) { // Do your delegate calls here as you please } }

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