- [NSOperationQueue операции] возвращает пустой массив, когда он не должен?
Я пишу приложение, которое выполняет асинхронную загрузку изображений на экран. Я настроил его не одновременно (то есть, он порождает поток и выполняет их по одному), поэтому я только переопределил функцию [NSOperation main]
в моем подклассе NSOperation.
В любом случае, поэтому, когда я добавляю все эти операции, я хочу, чтобы позже мог получить доступ к операциям в очереди, чтобы изменить их приоритеты. К сожалению, всякий раз, когда я называю -[NSOperationQueue operations]
, все, что я возвращаю, это пустой массив. Самое приятное то, что после ввода некоторых консольных операторов печати потоки все еще находятся в очереди и выполняются (обозначены отпечатками), несмотря на то, что массив пуст!
Что дает? Я также посмотрел на этот счет, чтобы убедиться, что все они не выполняются сразу, и это, похоже, не так.
Любые идеи? Вытягивая мои волосы на этом.
EDIT: Также стоит упомянуть, что тот же код предоставляет полный массив при запуске в симуляторе: (
Ответы
Ответ 1
Я прошел через -operations
и обнаружил, что он в основном выполняет:
[self->data->lock lock];
NSString* copy = [[self->data->operations copy] autorelease];
[self->data->lock unlock];
return copy;
кроме, после вызова -autorelease
последующие инструкции перезаписывают регистр, содержащий единственный указатель на новую копию очереди операций. Затем вызывающий абонент получает возвращаемое значение nil
. Поле "data
" является экземпляром внутреннего класса с именем _NSOperationQueueData
, который имеет поля:
NSRecursiveLock* lock;
NSArray* operations;
Мое решение заключалось в подклассе и переопределении -operations
, следуя той же логике, но фактически возвращающей копию массива. Я добавил некоторые проверки работоспособности, чтобы помочь, если внутренние элементы NSOperationQueue
не совместимы с этим исправлением. Эта повторная реализация вызывается только в том случае, если вызов [super operations]
действительно возвращает nil
.
Это может сломаться в будущих выпусках ОС, если Apple изменит внутреннюю структуру, но каким-то образом избежать фактической фиксации этой ошибки.
#if TARGET_OS_IPHONE
#import <objc/runtime.h>
@interface _DLOperationQueueData : NSObject {
@public
id lock; // <NSLocking>
NSArray* operations;
}
@end
@implementation _DLOperationQueueData; @end
@interface _DLOperationQueueFix : NSObject {
@public
_DLOperationQueueData* data;
}
@end
@implementation _DLOperationQueueFix; @end
#endif
@implementation DLOperationQueue
#if TARGET_OS_IPHONE
-(NSArray*) operations
{
NSArray* operations = [super operations];
if (operations != nil) {
return operations;
}
_DLOperationQueueFix* fix = (_DLOperationQueueFix*) self;
_DLOperationQueueData* data = fix->data;
if (strcmp(class_getName([data class]), "_NSOperationQueueData") != 0) {
// this hack knows only the structure of _NSOperationQueueData
// anything else, bail
return operations;
}
if ([data->lock conformsToProtocol: @protocol(NSLocking)] == NO) {
return operations; // not a lock, bail
}
[data->lock lock];
operations = [[data->operations copy] autorelease];
[data->lock unlock];
return operations; // you forgot something, Apple.
}
#endif
@end
Файл заголовка:
@interface DLOperationQueue : NSOperationQueue {}
#if TARGET_OS_IPHONE
-(NSArray*) operations;
#endif
@end
Ответ 2
Я просто не верю, что здесь достаточно контекста, чтобы сказать, что происходит. Очевидно, что-то не так, но вы не говорите, как вы ограничиваете concurrency, как вы тестируете, чтобы увидеть, что объекты запущены и т.д.
Что касается симулятора и iPhone, то NSOperations может действовать совершенно по-разному между этими двумя, поскольку все Mac на базе Intel являются многопроцессорными, и никаких iPhone не существует. В зависимости от того, как вы пытаетесь ограничить concurrency, вы можете оказаться в ситуации, когда невозможно выполнить выполнение на втором ядре, запрещает запуск файлов и т.д. Но без каких-либо подробностей это невозможно узнать.
Ответ 3
Не знаю, почему вы видите это поведение, но в качестве обходного пути вы можете сохранить свои собственные ссылки на отдельные операции, поскольку они добавлены в очередь.
Ответ 4
Я видел подобное поведение в ситуациях с низкой памятью. Сколько памяти вы используете? Вы правильно очищаете кеши и другие временные данные, когда получаете сообщение didReceiveMemoryWarning?
Ответ 5
Я столкнулся с одной и той же проблемой. Простой код, чем я использую в приложении OS X, и все же [операции myoperationqueue] всегда возвращает nil. Я планировал использовать это, чтобы избежать дублирования запросов. Это на iPhone OS 2.2.1. Конечно, похоже на ошибку. Спасибо за код, я могу использовать его или просто использовать собственное зеркало очереди.
Это не на симуляторе, и я подтверждаю, что добавляю 20 или точные копии задания, которые все выстраиваются красиво и делают работу в 19 раз слишком много!
Это действительно довольно простой код. Я почти не использую память - это при запуске приложения, которое пока не имеет ui.
- Том