Я прошел через -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