Я пишу приложение, которое выполняет асинхронную загрузку изображений на экран. Я настроил его НЕ параллельно (то есть он порождает потоки и выполняет их по одному), поэтому я только переопределил функцию [NSOperation main] в моем подклассе NSOperation.
В любом случае, когда я добавляю все эти операции, я хочу иметь возможность позже получить доступ к операциям в очереди, чтобы изменить их приоритеты. К сожалению, всякий раз, когда я звоню -[NSOperationQueue operations], все, что я получаю, - это пустой массив. Самое приятное то, что после ввода некоторых консольных операторов печати потоки все еще находятся в очереди и выполняются (обозначены печатью), несмотря на то, что массив пуст!
Что дает? Я также посмотрел на adcount, чтобы убедиться, что все они не выполняются одновременно, и, похоже, это не так.
Есть идеи? Выдергивал мои волосы на этом.
Обновлено: Также стоит упомянуть, что тот же код предоставляет полный массив при запуске в симуляторе :(





Понятия не имею, почему вы наблюдаете такое поведение, но в качестве чистого обходного пути вы можете сохранить свои собственные ссылки на отдельные операции по мере их добавления в очередь.
Я просто не верю, что здесь достаточно контекста, чтобы сказать, что происходит. Ясно, что что-то не так, но вы не говорите, как вы ограничиваете параллелизм, как вы тестируете, чтобы увидеть, как работают объекты и т. д.
Что касается симулятора по сравнению с iPhone, NSOperations могут действовать совершенно по-разному между ними, поскольку все Mac на базе Intel являются многопроцессорными, а iPhone - нет. В зависимости от того, как вы пытаетесь ограничить параллелизм, вы можете оказаться в ситуации, когда невозможность выполнения на втором ядре мешает запуску чего-либо и т. д. Но без дополнительных деталей это невозможно узнать.
Я видел подобное поведение в ситуациях с нехваткой памяти. Сколько памяти вы используете? Правильно ли вы очищаете кеши и другие временные данные, когда получаете сообщение didReceiveMemoryWarning?
Я прошел через -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
Я не отправлял ни одного, но отправил сейчас. Идентификатор ошибки №6643022.
Я столкнулся с той же проблемой. Более простой код, чем я использую в приложении OS X, и тем не менее [myoperationqueue operations] всегда возвращает nil. Я планировал использовать это, чтобы избежать дублирования запросов. Это на iPhone OS 2.2.1. Конечно похоже на ошибку. Спасибо за код, я могу его использовать или просто использовать собственное зеркало очереди.
Этого нет в симуляторе, и я подтверждаю, что добавляю 20 или точно таких же копий задания, которые хорошо выстраиваются в линию и выполняют задание в 19 раз больше!
Это действительно довольно простой код. Я почти не использую память - это при запуске приложения, в котором еще нет пользовательского интерфейса.
--Том
Сообщали ли вы об этом на bugreport.apple.com, и если да, то каков номер ошибки?