[Операции NSOperationQueue] возвращает пустой массив, когда это не должно? - PullRequest
12 голосов
/ 30 октября 2008

Я пишу приложение, которое выполняет асинхронную загрузку изображений на экран. Я настроил его так, чтобы он НЕ был параллельным (то есть он порождает поток и выполняет их по одному), поэтому я переопределил только функцию [NSOperation main] в моем подклассе NSOperation.

В любом случае, поэтому, когда я добавляю все эти операции, я хочу иметь возможность позже получить доступ к операциям в очереди, чтобы изменить их приоритеты. К сожалению, всякий раз, когда я вызываю -[NSOperationQueue operations], я получаю только пустой массив. Самое приятное то, что после добавления некоторых операторов печати консоли потоки все еще находятся в очереди и исполняются (обозначается печатью), несмотря на то, что массив пуст!

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

Есть идеи? Вытаскиваю на этом мои волосы.

РЕДАКТИРОВАТЬ: Стоит также отметить, что тот же код обеспечивает полный массив при запуске в симуляторе: (

Ответы [ 5 ]

8 голосов
/ 04 марта 2009

Я прошел через -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
1 голос
/ 04 ноября 2008

Я просто не верю, что здесь достаточно контекста, чтобы сказать, что происходит. Очевидно, что что-то не так, но вы не говорите, как вы ограничиваете параллелизм, как вы тестируете, чтобы увидеть, что объекты работают, и т. Д.

Что касается симулятора против iPhone, NSOperations может действовать совершенно по-разному между двумя, так как все Mac на базе Intel являются многопроцессорными, а iPhone нет. В зависимости от того, как вы пытаетесь ограничить параллелизм, вы можете оказаться в ситуации, когда невозможность выполнения на втором ядре мешает запуску чего-либо и т. Д. Но без подробностей это невозможно узнать.

0 голосов
/ 14 мая 2009

Я просто столкнулся с той же проблемой. Более простой код, чем я использую в приложении OS X, и все же [операции myoperationqueue] всегда возвращает ноль. Я планировал использовать это, чтобы избежать дублирования запросов. Это на iPhone OS 2.2.1. Конечно, похоже на ошибку. Спасибо за код, я могу использовать его или просто использовать свое собственное зеркало очереди.

Это не на симуляторе, и я подтверждаю, что я добавляю 20 или точно такие же копии задания, которые все выстраиваются в очередь и выполняют работу в 19 раз больше!

Это действительно довольно простой код. Я почти не использую память - это при запуске приложения, которое еще не имеет пользовательского интерфейса.

- Том

0 голосов
/ 05 ноября 2008

Я видел похожее поведение в ситуациях с нехваткой памяти. Сколько памяти вы используете? Правильно ли вы очищаете кеш и другие временные данные, когда получаете сообщение didReceiveMemoryWarning ?

0 голосов
/ 01 ноября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...