Категории NSOperation + Objective-C = Плохая идея?

Я установил категорию Objective-C для класса UIImageView приложения iPhone. Миссия категории - помочь асинхронно загружать изображения на основе URL с кэшированием памяти / диска.

Теперь в UIImageView + Cache.m у меня есть доступ к NSOperationQueue, поэтому я могу начать поток загрузки. Я создаю объект, производный от NSOperation, инициализируемый URL-адресом изображения и целевым UIImageView, а также селектор для выполнения на целевом объекте после завершения операции. В методе селектора мы устанавливаем наше только что загруженное изображение (или, если оно не найдено, мы устанавливаем альтернативное изображение-заполнитель), и все готово!

Это работает нормально, пока UIImageView не будет удален до завершения NSOperation. Например, у меня есть предыдущий / следующий сегментированный элемент управления в моем пользовательском интерфейсе, который приводит к тому, что эти UIImageViews удаляются и добавляются заново (они являются частью более крупного «элемента», который просматривается в приложении), поэтому его очень легко нажать они в быстрой последовательности.

Так что, если вы решили начать постукивать до того, как все изображения загрузятся - КАБЛАМ! Несчастный поток имеет недопустимый объект и не знает об этом. :(

Самое близкое, что я могу найти, чтобы помочь смягчить это, - это методы cancel и isCancelled NSOperation, за исключением того, что вы не можете отслеживать, какой объект операции нужно отменить в категории, потому что - если я правильно понял - категории не могут добавлять IVAR к объектам!

Может быть, это означает, что категории здесь не лучшая идея? (Хнычет: "Но я люблю категории! Ваааа!")

Совет приветствуется!


person Joe D'Andrea    schedule 09.07.2009    source источник


Ответы (3)


Я бы, наверное, не стал использовать категорию для этой ситуации. Категории полезны, но обычно в них нет необходимости. Я бы использовал категорию только в том случае, если у вас есть для этого действительно веская причина. Что именно вы помещаете в категорию?

Я думаю, вы могли бы реализовать все это в подклассе NSOperation, что было бы лучшим решением. Сохраните представление изображения, чтобы оно не было освобождено до загрузки изображения, и отмените загрузку, если представление больше не отображается. Если это невозможно, создайте подкласс UIImageView вместо использования категории.

person Tom Dalling    schedule 09.07.2009
comment
Категория добавляет к UIImageView два метода: loadWithCachedImageForURL: (NSURL *) url и didFinishLoadingImageWithResult: (NSDictionary *) result. Существует также подкласс NSOperation с именем ImageLoadingOperation, который используется loadWithCachedImageForURL при запуске операции. (Очередь определена где-то еще в одноэлементном классе, используемом для нескольких общих объектов.) Между тем идея сохранения звучит так, как будто она может сработать! Я не думал об этом. Я отчитаюсь - спасибо! - person Joe D'Andrea; 09.07.2009
comment
Использование сохранения помогло! Спасибо! - person Joe D'Andrea; 09.07.2009
comment
Хорошо, оставь / отпусти. ;) Что касается проверки видимости, то операция не знает, что это за цель (UIImageView и т. Д.). Теперь, я полагаю, я мог проверить, что это за объект, но операция изначально была разработана для работы с любым объектом, а не только с изображениями. В настоящее время я продолжаю загружать образ независимо, и делаю сохранение в init и выпуск в dealloc (для NSOperation). Есть смысл? - person Joe D'Andrea; 09.07.2009
comment
Звучит отлично. Единственный недостаток заключается в том, что вы можете загружать изображения, которые никогда не будут отображаться. Если пользователь много листает, это может стать проблемой. Возможно, вы могли бы отменить на основе необязательного метода, например: if ([targetObject responsedsToSelector: @selector (hasBeenInvalidated)]) {if ([targetObject hasBeenInvalidated]) {[self cancel]; }} - person Tom Dalling; 09.07.2009
comment
Возвращаясь к первой части вашего ответа, я все еще использую категорию, которая, в свою очередь, за кулисами использует NSOperation. Идея заключалась / заключается в том, чтобы наделить UIImageView дополнительным методом, который загружает / кэширует изображение асинхронно, чтобы NSOperation использовался в одном месте, а не везде, где у меня есть UIImageView. Возможно, это все еще опрометчиво? Если да, то как я могу сделать это чисто, используя only NSOperation, сохраняя при этом все мои экземпляры UIImageView аккуратными и аккуратными в другом месте? - person Joe D'Andrea; 09.07.2009
comment
Упс, я ответил, пока вы отвечали. Собственно - вот и недостаток. Уловка теперь заключается в том, чтобы выяснить, как настроить этот селектор. (UIImageViews часто удаляются вследствие удаления содержащего их супервизора, такого как UIScrollView.) По сути, я пытаюсь сделать его самодостаточным, чтобы это была операция типа «установил и забыл» . Возможно, тогда я заставляю операцию требовать чего-то типа UIView, а не просто идентификатора. Тогда я знаю, что могу проверить с точки зрения собственности. :) - person Joe D'Andrea; 09.07.2009
comment
Да, вы можете заставить NSOperation принимать NSView и отменить, если [view superview] равен нулю. Что касается избавления от категории, вы можете сделать функцию в ImageLoadingOperation, например + [ImageLoadingOperation loadImageFromUrl: (NSURL *) url intoView: (NSView *) view usingSelector: (SEL) selectorToCallWithResult]; Он будет создавать и запускать ImageLoadingOperation с указанными аргументами. - person Tom Dalling; 09.07.2009
comment
Хм! Мне нравится, как это звучит. Итак, я бы добавил метод класса к моему классу ImageLoadingOperation, который создает экземпляр самого как новую операцию, которая затем проходит через обычную процедуру init / main / dealloc. Я мог бы перенести туда и кеширование. - person Joe D'Andrea; 09.07.2009
comment
Что мне понравилось в использовании категорий, так это то, что стало проще переключаться между кэшированными и некэшированными нагрузками. Код выглядит почти идентично. Просто метод меняется. Тем не менее, это интересный вариант, и он устраняет одну пару исходных файлов. - person Joe D'Andrea; 09.07.2009
comment
Ой! Ждать. Нам все еще нужно что-то для обработки селектора после завершения загрузки. Но теперь этот селектор не инкапсулирован в категорию. Таким образом, он удаляет категорию, но мне нужно обрабатывать этот селектор везде, где я использую эту операцию. Хорошо, тогда. Решения, решения. :) - person Joe D'Andrea; 09.07.2009

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

person teabot    schedule 09.07.2009
comment
Я не хочу сказать, что колеблюсь. Мне просто интересно, хорошая ли это идея в целом с точки зрения дизайна. - person Joe D'Andrea; 09.07.2009
comment
Что касается использования подкласса, вот один пример, который я только что нашел! Еще не пробовал, но хотел поделиться для тех, кто играет дома: markj .net / iphone-asynchronous-table-image. - person Joe D'Andrea; 09.07.2009

Сохраняете ли вы UIImageView NSOperation? В противном случае imageView может быть освобожден до завершения NSOperation, что приведет к kablooi central. Вы должны выполнить сохранение, а затем, как только вы закончите setImage, выполнить релиз.

person AlBlue    schedule 23.07.2009
comment
Да, конечно! Представление изображения сохраняется в инициализации NSOperation (присваивается self.view, где представление является неатомарным / сохраняемым свойством). Интересный момент (который вы / я уже знаете, но случайные читатели, плохо знакомые с Objective-C, могут не знать): view = theView - это не то же самое, что self.view = theView, когда задействованы синтезированные свойства. Последний вызывает метод установки (и все преимущества, которые он предоставляет). Первый назначается ивару напрямую - без удержания! На данный момент я использую подкласс UIImageView, который вызывает запрос NSURLRequest requestWithURL: cachePolicy: timeoutInterval :. - person Joe D'Andrea; 23.07.2009