10

ブロックを返すメソッドを使用する場合、非常に便利です。ただし、それらのいくつかをつなぎ合わせる必要がある場合は、すぐに面倒になります

たとえば、4 つの URL を連続して呼び出す必要があります。

[remoteAPIWithURL:url1 success:^(int status){
    [remoteAPIWithURL:url2 success:^(int status){
        [remoteAPIWithURL:url3 success:^(int status){
            [remoteAPIWithURL:url2 success:^(int status){
            //succes!!!
            }];
        }];
    }];
}];

そのため、反復ごとに 1 レベル深くなり、ネストされたブロックのエラーはまだ処理していません。

実際のループがあるとさらに悪化します。たとえば、ファイルを 100 個のチャンクでアップロードしたいとします。

- (void) continueUploadWithBlockNr:(int)blockNr
{
    if(blocknr>=100) 
    {
    //success!!!
    }
    [remoteAPIUploadFile:file withBlockNr:blockNr success:^(int status)
    {
        [self continueUploadWithBlockNr:blockNr];
    }];
}

これは非常に直感的ではなく、すぐに非常に読みにくくなります。

.Net では、これらすべてを async および await キーワードを使用して解決し、基本的にこれらの継続を一見同期フローに展開しました。

Objective C のベスト プラクティスは何ですか?

4

8 に答える 8

4

あなたの質問はすぐに再帰について考えさせられました。結局のところ、Objective-cブロックは再帰的に使用できます。そこで、私は次のソリューションを思いつきました。これは理解しやすく、N個のタスクに非常にうまく拡張できます。

// __block declaration of the block makes it possible to call the block from within itself
__block void (^urlFetchBlock)();

// Neatly aggregate all the urls you wish to fetch
NSArray *urlArray = @[
    [NSURL URLWithString:@"http://www.google.com"],
    [NSURL URLWithString:@"http://www.stackoverflow.com"],
    [NSURL URLWithString:@"http://www.bing.com"],
    [NSURL URLWithString:@"http://www.apple.com"]
];
__block int urlIndex = 0;

// the 'recursive' block 
urlFetchBlock = [^void () {
    if (urlIndex < (int)[urlArray count]){
        [self remoteAPIWithURL:[urlArray objectAtIndex:index] 
            success:^(int theStatus){
                urlIndex++;
                urlFetchBlock();
            }

            failure:^(){
                // handle error. 
            }];
    }
} copy];

// initiate the url requests
urlFetchBlock();
于 2012-11-20T12:56:50.973 に答える
2

入れ子を減らす 1 つの方法は、個々のブロックを返すメソッドを定義することです。クロージャを介して Objective C コンパイラによって「自動的に」行われるデータ共有を促進するには、共有状態を保持する別のクラスを定義する必要があります。

これを行う方法の大まかなスケッチを次に示します。

typedef void (^WithStatus)(int);

@interface AsyncHandler : NSObject {
    NSString *_sharedString;
    NSURL *_innerUrl;
    NSURL *_middleUrl;
    WithStatus _innermostBlock;
}
+(void)handleRequest:(WithStatus)innermostBlock
            outerUrl:(NSURL*)outerUrl
            middleUrl:(NSURL*)middleUrl
            innerUrl:(NSURL*)innerUrl;

-(WithStatus)outerBlock;

-(WithStatus)middleBlock;

@end

@implementation AsyncHandler

+(void)handleRequest:(WithStatus)innermostBlock
            outerUrl:(NSURL*)outerUrl
            middleUrl:(NSURL*)middleUrl
            innerUrl:(NSURL*)innerUrl {
    AsyncHandler *h = [[AsyncHandler alloc] init];
    h->_innermostBlock = innermostBlock;
    h->_innerUrl = innerUrl;
    h->_middleUrl = middleUrl;
    [remoteAPIWithURL:outerUrl success:[self outerBlock]];
}

-(WithStatus)outerBlock {
    return ^(int success) {
        _sharedString = [NSString stringWithFormat:@"Outer: %i", success];
        [remoteAPIWithURL:_middleUrl success:[self middleBlock]];
    };
}

-(WithStatus)middleBlock {
    return ^(int success) {
        NSLog("Shared string: %@", _sharedString);
        [remoteAPIWithURL:_innerUrl success:_innermostBlock];
    };
}

@end

注: これはすべて ARC を前提としています。それなしでコンパイルする場合はBlock_copy、ブロックを返すメソッドで使用する必要があります。また、以下の呼び出しコードでコピーを行う必要があります。

これで、次のように、 「ロシア人形」のネストなしで元の関数を書き直すことができます。

[AsyncHandler
    handleRequest:^(int status){
        //succes!!!
    }
    outerUrl:[NSURL @"http://my.first.url.com"]
    middleUrl:[NSURL @"http://my.second.url.com"]
    innerUrl:[NSURL @"http://my.third.url.com"]
];
于 2012-11-13T23:00:24.377 に答える
2

反復アルゴリズム:

  • __block変数 ( ) を作成してint urlNum、現在の URL (それらの 内) を追跡しますNSArray
  • すべての URL が読み込まれるまで、onUrlComplete ブロックが次のリクエストを開始するようにします。
  • 最初のリクエストを起動します。
  • すべての URL が読み込まれたら、「//success!」を実行します。ダンス。

XCode を使用せずに記述されたコード (つまり、コンパイル エラーが発生する可能性があります。必要に応じて修正されます):

- (void)loadUrlsAsynchronouslyIterative:(NSArray *)urls {
  __block int urlNum = 0;
  void(^onUrlComplete)(int) = nil; //I don't remember if you can call a block from inside itself.
  onUrlComplete = ^(int status) {
    if (urlNum < urls.count) {
      id nextUrl = urls[urlNum++];
      [remoteAPIWithURL:nextUrl success:onUrlComplete];
    } else {
      //success!
    }
  }
  onUrlComplete(0); //fire first request
}

再帰アルゴリズム:

  • 残りのすべての URL をロードするメソッドを作成します。
  • 残りの URL が空になったら、「onSuccess」を起動します。
  • それ以外の場合は、次の URL の要求を起動し、最初の残りの URL を除くすべてのメソッドを再帰的に呼び出す完了ブロックを提供します。
  • 複雑な問題: パラメータを受け入れるように「onSuccess」ブロックを宣言したint statusため、最後のステータス変数 (「デフォルト」値を含む) を渡します。

XCode を使用せずに記述されたコード (バグの免責事項はこちら):

- (void)loadUrlsAsynchronouslyRecursive:(NSArray *)remainingUrls onSuccess:(void(^)(int status))onSuccess lastStatus:(int)lastStatus {
  if (remainingUrls.count == 0) {
    onSuccess(lastStatus);
    return;
  }
  id nextUrl = remainingUrls[0];
  remainingUrls = [remainingUrls subarrayWithRange:NSMakeRange(1, remainingUrls.count-1)];
  [remoteAPIWithUrl:nextUrl onSuccess:^(int status) {
    [self loadUrlsAsynchronouslyRecursive:remainingUrls onSuccess:onSuccess lastStatus:status];
  }];
}

//fire first request:
[self loadUrlsAsynchronouslyRecursive:urls onSuccess:^(int status) {
  //success here!
} lastStatus:0];

どちらが良いですか?

  • __block変数とスコープを使ったゲームに慣れている場合は、反復アルゴリズムは単純で簡潔です。
  • あるいは、再帰アルゴリズムは__block変数を必要とせず、再帰アルゴリズムが進むにつれてかなり単純になります。
  • 再帰的な実装は、(実装された) 反復的な実装よりも再利用可能です。
  • 再帰アルゴリズムはリークする可能性があります ( への参照が必要ですself) が、それを修正するにはいくつかの方法があります: 関数にする、 を使用する__weak id weakSelf = self;などです。

エラー処理を追加するのはどれくらい簡単でしょうか?

  • 反復実装は、ブロックがより複雑になるstatusという犠牲を払って、の値をチェックするように簡単に拡張できます。onUrlComplete
  • 再帰的な実装は、おそらく拡張が簡単ではありません。主な理由は、再利用できるからです。ステータスがこのような場合、これ以上の URL のロードをキャンセルしますか? int status次に、受け入れて返すBOOL(たとえばYES、続行NOする、キャンセルする)ステータス チェック/エラー処理ブロックを渡します。または、両方をonSuccess受け入れるように変更することもできますが、ブロックの実装を呼び出す必要があります。int statusNSArray *remainingUrlsloadUrlsAsynchronouslyRecursive...onSuccess
于 2012-11-17T18:12:09.700 に答える
1

あなたは(コメントで)「非同期メソッドは、明示的なスレッドを使用せずに簡単な非同期性を提供します」と述べました。しかし、あなたの不満は、非同期メソッドで何かをしようとしているようで、簡単ではありません。ここに矛盾があると思いますか?

コールバック ベースの設計を使用すると、言語の組み込み構造を使用して制御フローを直接表現する機能が犠牲になります。

したがって、コールバック ベースの設計の使用をやめることをお勧めします。グランド セントラル ディスパッチ (GCD) を使用すると、"バックグラウンドで" 作業を実行し、メイン スレッドにコールバックしてユーザー インターフェイスを更新することが簡単になります (この言葉もまた!)。したがって、API の同期バージョンがある場合は、それをバックグラウンド キューで使用するだけです。

- (void)interactWithRemoteAPI:(id<RemoteAPI>)remoteAPI {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // This block runs on a background queue, so it doesn't block the main thread.
        // But it can't touch the user interface.

        for (NSURL *url in @[url1, url2, url3, url4]) {
            int status = [remoteAPI syncRequestWithURL:url];
            if (status != 0) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    // This block runs on the main thread, so it can update the
                    // user interface.
                    [self remoteRequestFailedWithURL:url status:status];
                });
                return;
            }
        }
    });
}

通常の制御フローを使用しているだけなので、より複雑なことを行うのは簡単です。2 つのリクエストを発行し、最大 100k のチャンクでファイルをアップロードしてから、もう 1 つのリクエストを発行する必要があるとします。

#define AsyncToMain(Block) dispatch_async(dispatch_get_main_queue(), Block)

- (void)uploadFile:(NSFileHandle *)fileHandle withRemoteAPI:(id<RemoteAPI>)remoteAPI {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        int status = [remoteAPI syncRequestWithURL:url1];
        if (status != 0) {
            AsyncToMain(^{ [self remoteRequestFailedWithURL:url1 status:status]; });
            return;
        }

        status = [remoteAPI syncRequestWithURL:url2];
        if (status != 0) {
            AsyncToMain(^{ [self remoteRequestFailedWithURL:url2 status:status]; });
            return;
        }

        while (1) {
            // Manage an autorelease pool to avoid accumulating all of the
            // 100k chunks in memory simultaneously.
            @autoreleasepool {
                NSData *chunk = [fileHandle readDataOfLength:100 * 1024];
                if (chunk.length == 0)
                    break;
                status = [remoteAPI syncUploadChunk:chunk];
                if (status != 0) {
                    AsyncToMain(^{ [self sendChunkFailedWithStatus:status]; });
                    return;
                }
            }
        }

        status = [remoteAPI syncRequestWithURL:url4];
        if (status != 0) {
            AsyncToMain(^{ [self remoteRequestFailedWithURL:url4 status:status]; });
            return;
        }

        AsyncToMain(^{ [self uploadFileSucceeded]; });
    });
}

きっとあなたは「ああ、それは素晴らしいですね」と言っているに違いありません。RemoteAPI;^) しかし、「同期メソッドではなく、非同期メソッドしかない 場合はどうなるでしょうか?」と言うかもしれません。</p>

GCD を使用して、非同期メソッドの同期ラッパーを作成できます。ラッパーが async メソッドを呼び出すようにし、async メソッドがコールバックを呼び出すまでブロックする必要があります。ややこしいのは、非同期メソッドがコールバックを呼び出すためにどのキューを使用するか、またそれがコールバックを呼び出すために使用するかどうかがわからないことですdispatch_sync。したがって、並行キューから async メソッドを呼び出すことで安全を確保しましょう。

- (int)syncRequestWithRemoteAPI:(id<RemoteAPI>)remoteAPI url:(NSURL *)url {
    __block int outerStatus;
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    [remoteAPI asyncRequestWithURL:url completion:^(int status) {
        outerStatus = status;
        dispatch_semaphore_signal(sem);
    }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    dispatch_release(sem);
    return outerStatus;
}

アップデート

最初に 3 番目のコメントに返信し、次に 2 番目のコメントに返信します。

3 番目のコメント

3 番目のコメント:

最後になりましたが、別のスレッドを専用の呼び出しの同期バージョンをラップするソリューションは、非同期の代替手段を使用するよりもコストがかかります。スレッドは高価なリソースであり、ブロックしているときは基本的に 1 つのスレッドを失っています。非同期呼び出し (少なくとも OS ライブラリ内のもの) は、通常、はるかに効率的な方法で処理されます。(たとえば、同時に 10 個の URL を要求した場合、10 個のスレッドを起動しない可能性があります (またはそれらをスレッドプールに入れません))。

はい、スレッドを使用すると、非同期呼び出しを使用するよりもコストがかかります。だから何?問題は、それが高すぎるかどうかです。Objective-C メッセージは、現在の iOS ハードウェアの一部のシナリオ (たとえば、リアルタイムの顔検出アルゴリズムや音声認識アルゴリズムの内部ループ) ではコストがかかりすぎますが、ほとんどの場合、それらを使用することに何の不安もありません。

スレッドが「高価なリソース」であるかどうかは、コンテキストに依存します。あなたの例を考えてみましょう:「たとえば、同時に 10 個の URL を要求した場合、10 個のスレッドを起動しない可能性があります (またはそれらをスレッドプールに入れません)」。確認してみましょう。

NSURL *url = [NSURL URLWithString:@"http://1.1.1.1/"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
for (int i = 0; i < 10; ++i) {
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSLog(@"response=%@ error=%@", response, error);
    }];
}

ここでは、Apple 独自の推奨+[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]方法を使用して、10 個のリクエストを非同期に送信しています。私は URL を非応答にするように選択したので、Apple がこのメソッドを実装するためにどのような種類のスレッド/キュー戦略を使用しているかを正確に知ることができます。iOS 6.0.1 を実行している iPhone 4S でアプリを実行し、デバッガーで一時停止して、スレッド ナビゲーターのスクリーン ショットを撮りました。

10 NSURLConnection sendAsynchronousRequest: スレッド

というラベルのスレッドが 10 個あることがわかりますcom.apple.root.default-priority。そのうちの 3 つを開いたので、それらが通常の GCD キュー スレッドであることがわかります。それぞれが で定義されたブロック+[NSURLConnection sendAsynchronousRequest:…]を呼び出します+[NSURLConnection sendSynchronousRequest:…]。10 個すべてを確認しましたが、スタック トレースはすべて同じです。したがって、実際には、OS ライブラリは 10 個のスレッドをスピンアップします。

ループ数を 10 から 100 に増やしたところ、GCD がcom.apple.root.default-priorityスレッド数を 64 に制限していることがわかりました。したがって、私が発行した他の 36 の要求は、グローバルなデフォルト優先度キューに入れられ、実行が開始されるまでは開始されません。 64 の「実行中」のリクエストの一部が終了します。

では、スレッドを使用して非同期関数を同期関数に変えるのはコストが高すぎるのでしょうか? これらを同時にいくつ行うかにもよると思います。数が 10 を下回っても、20 を下回っても、何の心配もありません。

2 番目のコメント

これにより、2番目のコメントが表示されます。

ただし、これらの 3 つのことを同時に実行し、それらの「いずれか」が終了したら、残りを無視してこれら 3 つの呼び出しを同時に実行し、「すべて」が終了すると成功します。

これらは GCD を使用するのが簡単なケースですが、必要に応じて GCD と非同期のアプローチを組み合わせて、使用するスレッドを減らしながら、制御フローに言語ネイティブ ツールを使用することもできます。

最初に、後で入力する手間を省くために、リモート API 補完ブロックの typedef を作成します。

typedef void (^RemoteAPICompletionBlock)(int status);

制御フローを前と同じ方法で、メイン スレッドから同時実行キューに移動して開始します。

- (void)complexFlowWithRemoteAPI:(id<RemoteAPI>)remoteAPI {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

まず、3 つのリクエストを同時に発行し、そのうちの 1 つが成功するまで (または、おそらく 3 つすべてが失敗するまで) 待機します。

statusOfFirstRequestToSucceedでは、任意の数の非同期リモート API 要求を発行し、最初の要求が成功するまで待機する関数 があるとします。この関数は、各非同期リクエストの完了ブロックを提供します。しかし、異なるリクエストは異なる引数を受け取る可能性があります... API リクエストを関数に渡すにはどうすればよいでしょうか?

APIリクエストごとにリテラルブロックを渡すことでそれを行うことができます。各リテラル ブロックは完了ブロックを受け取り、非同期リモート API 要求を発行します。

        int status = statusOfFirstRequestToSucceed(@[
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI requestWithCompletion:completion];
            },
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI anotherRequestWithCompletion:completion];
            },
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI thirdRequestWithCompletion:completion];
            }
        ]);
        if (status != 0) {
            AsyncToMain(^{ [self complexFlowFailedOnFirstRoundWithStatus:status]; });
            return;
        }

OK、これで最初の 3 つの並列リクエストを発行し、1 つが成功するか、すべてが失敗するのを待ちました。次に、さらに 3 つの並列リクエストを発行し、すべてが成功するか、そのうちの 1 つが失敗するのを待ちます。したがって、関数を想定することを除いて、ほぼ同じですstatusOfFirstRequestToFail

        status = statusOfFirstRequestToFail(@[
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI requestWithCompletion:completion];
            },
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI anotherRequestWithCompletion:completion];
            },
            ^(RemoteAPICompletionBlock completion) {
                [remoteAPI thirdRequestWithCompletion:completion];
            }
        ]);
        if (status != 0) {
            AsyncToMain(^{ [self complexFlowFailedOnSecondRoundWithStatus:status]; });
            return;
        }

並列リクエストの両方のラウンドが終了したので、メイン スレッドに成功を通知できます。

        [self complexFlowSucceeded];
    });
}

全体として、これは非常に単純な制御フローのように思えます。実装する必要があるのは and だけstatusOfFirstRequestToSucceedですstatusOfFirstRequestToFail。余分なスレッドなしでそれらを実装できます。それらは非常に似ているため、実際の作業を行うヘルパー関数を両方とも呼び出すようにします。

static int statusOfFirstRequestToSucceed(NSArray *requestBlocks) {
    return statusOfFirstRequestWithStatusPassingTest(requestBlocks, ^BOOL (int status) {
        return status == 0;
    });
}

static int statusOfFirstRequestToFail(NSArray *requestBlocks) {
    return statusOfFirstRequestWithStatusPassingTest(requestBlocks, ^BOOL (int status) {
        return status != 0;
    });
}

ヘルパー関数では、競合状態を防ぐために、完了ブロックを実行するためのキューが必要です。

static int statusOfFirstRequestWithStatusPassingTest(NSArray *requestBlocks,
    BOOL (^statusTest)(int status))
{
    dispatch_queue_t completionQueue = dispatch_queue_create("remote API completion", 0);

completionQueueusingdispatch_syncにのみブロックを配置しdispatch_sync、キューがメイン キューでない限り、常に現在のスレッドでブロックを実行することに注意してください。

また、一部のリクエストが成功ステータスで完了したとき、またはすべてのリクエストが完了したときに外部関数を起動するために、セマフォも必要です。

    dispatch_semaphore_t enoughJobsCompleteSemaphore = dispatch_semaphore_create(0);

まだ完了していないジョブの数と、最後に完了したジョブのステータスを追跡します。

    __block int jobsLeft = requestBlocks.count;
    __block int outerStatus = 0;

jobsLeftが 0 になると、テストをパスするステータスに設定したか、すべてのジョブが完了したことを意味しますouterStatus。これは、待機が完了したかどうかを追跡する作業を行う完了ブロックです。リモート API が複数の完了ブロックを並行して (個別のスレッドまたは同時キューで) ディスパッチする場合に備えて、andへのcompletionQueueアクセスをシリアル化するためにすべてを実行します。jobsLeftouterStatus

    RemoteAPICompletionBlock completionBlock = ^(int status) {
        dispatch_sync(completionQueue, ^{

外部関数がまだ現在のジョブの完了を待っているかどうかを確認します。

            if (jobsLeft == 0) {
                // The outer function has already returned.
                return;
            }

次に、残りのジョブ数を減らし、完了したジョブのステータスを外部関数で利用できるようにします。

            --jobsLeft;
            outerStatus = status;

完了したジョブのステータスがテストに合格した場合、jobsLeft他のジョブが私のステータスを上書きしたり、外側の関数を単一化したりしないようにゼロに設定します。

            if (statusTest(status)) {
                // We have a winner.  Prevent other jobs from overwriting my status.
                jobsLeft = 0;
            }

待機するジョブが残っていない場合 (ジョブがすべて終了したか、このジョブのステータスがテストに合格したため)、外側の関数を起こします。

            if (jobsLeft == 0) {
                dispatch_semaphore_signal(enoughJobsCompleteSemaphore);
            }

最後に、キューとセマフォを解放します。(保持は後で、リクエスト ブロックをループして実行するときに行われます。)

            dispatch_release(completionQueue);
            dispatch_release(enoughJobsCompleteSemaphore);
        });
    };

これで完了ブロックは終了です。関数の残りの部分は簡単です。最初に各リクエスト ブロックを実行し、ダングリング リファレンスを防ぐためにキューとセマフォを保持します。

    for (void (^requestBlock)(RemoteAPICompletionBlock) in requestBlocks) {
        dispatch_retain(completionQueue); // balanced in completionBlock
        dispatch_retain(enoughJobsCompleteSemaphore); // balanced in completionBlock
        requestBlock(completionBlock);
    }

ARC を使用していて、展開ターゲットが iOS 6.0 以降の場合、retains は必要ないことに注意してください。

次に、ジョブの 1 つが私を起こし、キューとセマフォを解放し、私を目覚めさせたジョブのステータスを返すのを待ちます。

    dispatch_semaphore_wait(enoughJobsCompleteSemaphore, DISPATCH_TIME_FOREVER);
    dispatch_release(completionQueue);
    dispatch_release(enoughJobsCompleteSemaphore);
    return outerStatus;
}

の構造はかなり一般的であることに注意してください。それぞれが完了ブロックを呼び出してステータスstatusOfFirstRequestWithStatusPassingTestを渡す限り、任意の要求ブロックを渡すことができます。int関数を変更して、各リクエスト ブロックからのより複雑な結果を処理したり、未処理のリクエストをキャンセルしたりできます (キャンセル API がある場合)。

于 2012-11-17T20:32:18.473 に答える
0

これを自分で調査しているときに、Objective-CのReactiveExtensionsのポートにぶつかりました。Reactive Extensionsは、一連のイベントまたは非同期操作をクエリする機能を備えているようなものです。.NetとJavaScriptで大きな普及があったことは知っていますが、Objective-Cの移植もあるようです。

https://github.com/blog/1107-reactivecocoa-for-a-better-world

構文はトリッキーに見えます。iPhoneの開発に実際の経験があるのか​​、そしてそれが実際にこの問題をエレガントに解決するのかどうか疑問に思います。

于 2012-11-14T09:37:11.590 に答える
0

I tend to wrap big nested block cluster f**** like you describe in subclasses of NSOperation that describe what the overall behaviour that your big nest block cluster f*** is actually doing (rather than leaving them littered throughout other code).

For example if your following code:

[remoteAPIWithURL:url1 success:^(int status){
    [remoteAPIWithURL:url2 success:^(int status){
        [remoteAPIWithURL:url3 success:^(int status){
            [remoteAPIWithURL:url2 success:^(int status){
            //succes!!!
            }];
        }];
    }];
}];

is intended to get an authorise token and then sync something perhaps it would be an NSAuthorizedSyncOperation… I'm sure you get the gist. Benefits of this are nice tidy bundles of behaviour wrapped up in a class with one place to edit them if things change down the line. My 2¢.

于 2012-11-18T20:41:06.257 に答える
0

NSDocument では、シリアル化に次のメソッドを使用できます。

Serialization
– continueActivityUsingBlock:
– continueAsynchronousWorkOnMainThreadUsingBlock:
– performActivityWithSynchronousWaiting:usingBlock:
– performAsynchronousFileAccessUsingBlock:
– performSynchronousFileAccessUsingBlock:

私はこれを掘り下げているところですが、ここから始めるのが良いようです。

于 2012-11-20T15:38:36.293 に答える
0

それがあなたがどこで探しているのかわかりませんか? 配列内のすべてのオブジェクトは、完了するまでに異なる時間を必要としますが、すべてがキューに送信された順序で表示されます。

typedef int(^SumUpTill)(int);
SumUpTill sum = ^(int max){
    int i = 0;
    int result = 0;
    while (i < max) {
        result += i++;
    }
    return result;
};

dispatch_queue_t queue = dispatch_queue_create("com.dispatch.barrier.async", DISPATCH_QUEUE_CONCURRENT);
NSArray *urlArray = @[  [NSURL URLWithString:@"http://www.google.com"],
                        @"Test",
                        [sum copy],
                        [NSURL URLWithString:@"http://www.apple.com"]
];

[urlArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    dispatch_barrier_async(queue, ^{
        if ([obj isKindOfClass:[NSURL class]]) {
            NSURLRequest *request = [NSURLRequest requestWithURL:obj];
            NSURLResponse *response = nil;
            NSError *error = nil;
            [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
            NSLog(@"index = %d, response=%@ error=%@", idx, response, error);
        }
        else if ([obj isKindOfClass:[NSString class]]) {
            NSLog(@"index = %d, string %@", idx, obj);
        }
        else {
            NSInteger result = ((SumUpTill)obj)(1000000);
            NSLog(@"index = %d, result = %d", idx, result);
        }
    });
}];
于 2012-11-20T15:20:55.257 に答える