11

AppleのGrandCentralDispatch(GCD)は素晴らしいですが、iOS4.0以降でのみ動作します。Appleのドキュメントには、「[A]シリアル化された操作キューはGrand Central Dispatchのシリアルディスパッチキューとまったく同じ動作を提供しません」と記載されています(キューはFIFOではなく、順序は依存関係と優先順位によって決定されるため)。

GCDがリリースされる前のOSバージョンをサポートしながら、GCDのシリアルディスパッチキューと同じ効果を達成する正しい方法は何ですか?言い換えれば、4.0未満のバージョンをサポートしたいiOSアプリで単純なバックグラウンド処理(Webサービスリクエストの実行など)を処理するための推奨される方法は何ですか?

4

5 に答える 5

4

このPseudoSerialQueueはどうですか?これは、ディスパッチシリアルキューのような最小限の実装です。

#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];
于 2011-06-06T21:43:06.287 に答える
3

を使用してシミュレートしNSOperationQueue、タスク数を1に設定するだけです。

編集

-おっと、もっと注意深く読むべきだった。fifoソリューションは次のとおりです。

私は、iOS開発者の大多数があなたの状況で使用する方法を考えることができません。

私はスレッド化されたプログラムを書くことを恐れていないので、ここに1つの解決策があります:

  • 次のようなFIFOワーカーキューを作成します。
    • ロックをサポート
    • 1つのNSOperationQueueを保持します
    • の実装でfifoキューからワーカーをプルするように設計されたNSOperationサブクラスを保持しますmain。一度に存在できるのは1つだけです。
    • 実行するワーカーのNSArrayを保持します(ワーカーの定義はあなた次第です-それはNSInvocation、クラス、操作などです)

NSOperationサブクラスは、FIFOワーカーキューが使い果たされるまで、FIFOワーカーキューからワーカーをプルします。

fifoワークキューにワーカーがあり、アクティブな子操作がない場合、fifo作業キューは子操作を作成し、それを操作キューに追加します。

スレッド化されたプログラムを書くことに慣れていない場合、いくつかの落とし穴があります。このため、このソリューションはすべての人にとって理想的ではありませんが、必要なすべてのテクノロジをすでに使い慣れている場合は、このソリューションの作成にそれほど時間はかかりません。

幸運を

于 2011-05-27T22:57:21.133 に答える
3

人々は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];

実行ループのスレッドプログラミングガイドセクションによると:

Cocoaは、任意のスレッドでセレクターを実行できるカスタム入力ソースを定義します。...実行セレクター要求はターゲットスレッドでシリアル化され、1つのスレッドで複数のメソッドが実行されている場合に発生する可能性のある同期の問題の多くを軽減します。

したがって、明示的にシリアルキューがあります。もちろん、実行ループに永久に実行するように指示したので、私のものは素晴らしく書かれていません。後で終了できるものを好むかもしれませんが、これらは簡単に変更できます。

于 2011-07-05T21:25:24.593 に答える
2

NSOperationQueueのドキュメント作成者が言及するのを忘れたことがあり、実際にはそうではないのに、そのような実装は些細なことのように見えます。

最大同時操作数を1に設定すると、NSOperationsが同じスレッドからキューに追加された場合にのみシリアルであることが保証されます。

それがうまくいくので、私は別のオプションを使用しています。

異なるスレッドからNSOperationsを追加しますが、キューイングを管理するにはNSConditionを使用します。startOperationsは、performSelectorOnBackgroundThreadで呼び出すことができます(そして、ロックでメインスレッドをブロックしたくない場合もあります)。

startOperationsメソッドは、1つ以上のNSOperationsで構成される単一のジョブを表します。

- (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];
}

それでおしまい!

于 2011-06-03T04:56:19.650 に答える
0

とにかく処理がバックグラウンドで行われている場合、本当に厳密に順番に処理する必要がありますか?その場合、依存関係を設定するだけで同じ効果が得られるため、1は0に依存し、2は1に依存し、3は2に依存します。次に、操作キューはそれらを順番に処理するように強制されます。最大同時操作数を1に設定すると、キューもシリアルであることが保証されます。

于 2011-06-02T20:04:37.340 に答える