Эквивалент очереди последовательной отправки GCD в iOS 3.x - PullRequest
11 голосов
/ 28 мая 2011

Великий Apple Central Central Dispatch (GCD) великолепен, но работает только на iOS 4.0 или выше. Документация Apple гласит: «Сериализованная очередь операций [A] не обеспечивает такого же поведения, как и очередь последовательной отправки в Grand Central Dispatch» (поскольку очередь не FIFO, но порядок определяется зависимостями и приоритеты).

Как правильно достичь того же эффекта, что и очереди последовательной отправки GCD, при поддержке версий ОС до выпуска GCD? Или, другими словами, каков рекомендуемый способ обработки простой фоновой обработки (выполнение запросов веб-служб и т. Д.) В приложениях iOS, которые хотят поддерживать версии менее 4.0?

Ответы [ 5 ]

4 голосов
/ 07 июня 2011

Как насчет этого PseudoSerialQueue? Это минимальная реализация, такая как Dispatch Serial Queue.

#import <Foundation/Foundation.h>

@interface PseudoTask : NSObject
{
    id target_;
    SEL selector_;
    id queue_;
}

@property (nonatomic, readonly) id target;

- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
- (void)exec;
@end

@implementation PseudoTask

@synthesize target=target_;

- (id)initWithTarget:(id)target selector:(SEL)selector queue:(id)queue;
{
    self = [super init];
    if (self) {
        target_ = [target retain];
        selector_ = selector;
        queue_ = [queue retain];
    }
    return self;
}

- (void)exec
{
    [target_ performSelector:selector_];
}

- (void)dealloc
{
    [target_ release];
    [queue_ release];
}
@end

@interface PseudoSerialQueue : NSObject
{
    NSCondition *condition_;
    NSMutableArray *array_;
    NSThread *thread_;
}
- (void)addTask:(id)target selector:(SEL)selector;
@end

@implementation PseudoSerialQueue
- (id)init
{
    self = [super init];
    if (self) {
        array_ = [[NSMutableArray alloc] init];
        condition_ = [[NSCondition alloc] init];
        thread_ = [[NSThread alloc]
            initWithTarget:self selector:@selector(execQueue) object:nil];
        [thread_ start];
    }
    return self;
}

- (void)addTask:(id)target selector:(SEL)selector
{
    [condition_ lock];
    PseudoTask *task = [[PseudoTask alloc]
        initWithTarget:target selector:selector queue:self];
    [array_ addObject:task];
    [condition_ signal];
    [condition_ unlock];
}

- (void)quit
{
    [self addTask:nil selector:nil];
}

- (void)execQueue
{
    for (;;) {
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        [condition_ lock];
        while (array_.count == 0)
            [condition_ wait];
        PseudoTask *task = [array_ objectAtIndex:0];
        [array_ removeObjectAtIndex:0];
        [condition_ unlock];

        if (!task.target) {
            [pool drain];
            break;
        }

        [task exec];
        [task release];

        [pool drain];
    }
}

- (void)dealloc
{
    [array_ release];
    [condition_ release];
}
@end

Как использовать:

PseudoSerialQueue *q = [[[PseudoSerialQueue alloc] init] autorelease];
[q addTask:self selector:@selector(test0)];
[q addTask:self selector:@selector(test1)];
[q addTask:self selector:@selector(test2)];
[q quit];
3 голосов
/ 06 июля 2011

Похоже, что люди собираются приложить немало усилий, чтобы переписать NSRunloop.Согласно документации NSRunloop :

Ваше приложение не может ни создавать объекты NSRunLoop, ни явно управлять ими.У каждого объекта NSThread, включая основной поток приложения, есть объект NSRunLoop, автоматически создаваемый для него по мере необходимости.

Поэтому, безусловно, тривиальным ответом будет создание пригодной для использования очереди:

- (void)startRunLoop:(id)someObject
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [[NSRunLoop currentRunLoop] run];

    [pool release];
}

...

NSThread *serialDispatchThread = [[NSThread alloc] 
                   initWithTarget:self 
                   selector:@selector(startRunLoop:) 
                   object:nil];
[serialDispatchThread start];

Чтобы добавить задачу в очередь:

[object
    performSelector:@selector(whatever:) 
    onThread:serialDispatchThread
    withObject:someArgument
    waitUntilDone:NO];

В соответствии с разделом Руководство по программированию Threading в циклах выполнения :

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

Итак, у вас есть явно последовательная очередь.Конечно, мой не написан фантастически, потому что я сказал циклу выполнения работать вечно, и вы можете предпочесть тот, который вы можете прекратить позже, но это легко сделать.

3 голосов
/ 28 мая 2011

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

РЕДАКТИРОВАТЬ

- упс, следовало бы прочитать более внимательно.решение fifo следующее:

Я не могу придумать, каким образом большинство разработчиков ios будут использовать в вашей ситуации.

Я не боюсь писать многопоточные программы, поэтому вотодно решение:

  • создать рабочую очередь fifo, которая:
    • поддерживает блокировку
    • содержит одно NSOperationQueue
    • содержит подкласс NSOperation, предназначенный для выведения рабочих из очереди fifo при реализации main.только один может существовать за один раз.
    • содержит NSArray рабочих для запуска (определение работника зависит от вас - это NSInvocation, класс,операция, ...)

подкласс NSOperation вытягивает рабочих из рабочей очереди fifo до тех пор, пока рабочая очередь fifo не будет исчерпана.

когда работа fifoВ очереди есть рабочие и нет активных дочерних операций, она создает дочернюю операцию, добавляет ее в свою очередь операций.

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

удачи

2 голосов
/ 03 июня 2011

Есть вещи, которые автор документации NSOperationQueue забыл упомянуть, поэтому такая реализация кажется тривиальной, хотя на самом деле это не так.

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

Я использую другой вариант, потому что он просто работает.

Добавить NSOperations из разных потоков, но использовать NSCondition для управления очередями. startOperations может (и не должен блокировать основной поток блокировками) вызываться с executeSelectorOnBackgroundThread ...

Метод startOperations представляет одно задание, состоящее из одной или нескольких операций NSOperation.

- (void)startOperations
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [[AppDelegate condition] lock];

    while (![[[AppDelegate queue] operations] count] <= 0) 
    {
        [[AppDelegate condition] wait];
    }

    NSOperation *newOperation = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    NSOperation *newOperation1 = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation1];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    NSOperation *newOperation2 = [alloc, init]....;
    [[AppDelegate queue] addOperation:newOperation2];
    [[AppDelegate queue] waitUntilAllOperationsAreFinished]; // Don't forget this!

    // Add whatever number operations you need for this single job

    [[AppDelegate queue] signal];
    [[AppDelegate queue] unlock];

    [NotifyDelegate orWhatever]

    [pool drain];
}

Вот и все!

0 голосов
/ 03 июня 2011

Если обработка в любом случае выполняется в фоновом режиме, вам действительно нужно, чтобы она была строго в порядке? Если вы это сделаете, вы можете добиться того же эффекта, просто настроив свои зависимости так, чтобы 1 зависело от 0, 2 от 1, 3 от 2 и т. Д. Затем очередь операций вынуждена обрабатывать их по порядку. Установите максимальное число одновременных операций равным 1, и очередь также гарантированно будет последовательной.

...