6

私は現在 iPhone アプリに取り組んでおり、非同期動作をするサードパーティのライブラリを持っていますが、独自のクラスでラップして同期的に見せたいと思っています。

このライブラリの中心的なクラス (接続クラスと呼びましょう) には、デリゲート クラスのインスタンスのメソッドが呼び出されたときに最終的な結果が解決されるいくつかの関数があります。私がやろうとしているのは、このクラスとデリゲートをラップして、非同期ではなく同期に見えるようにすることです。Java でこれを行う場合、FutureTask または CountdownLatch を使用するか、単に join() を使用します。しかし、Objective C でこれを行う最善の方法がわかりません。

上記のデリゲート プロトコルに準拠する NSThread 拡張機能 NFCThread を作成することから始めました。アイデアは、init と NFCThread、NFCThread インスタンスを Connection の setDelegate メソッドに渡し、スレッドを開始してから、Connection で非同期メソッドを呼び出すというものです。NFCThread インスタンスの 3 つのデリゲート メソッドのいずれかが呼び出され、最終的にスレッドが終了すると予想されます。

結合をシミュレートするために、次のことを行いました。NFCThread に NSConditionalLock を追加しました。

joinLock = [[NSConditionLock alloc] initWithCondition:NO];

Connection の呼び出しの周りのコードは次のようになります。

NFCThread *t = [[NFCThread alloc] init];
[connection setDelegate:t];
[t start];

[connection openSession];
// Process errors, etc...

[t.joinLock lockWhenCondition:YES];
[t.joinLock unlock];
[t release];
[connection setDelegate:nil];

デリゲートのプロトコルには 3 つのメソッドがあります。NFCThread では、各メソッドを次のように実装しました。

- (void)didReceiveMessage:(CommandType)cmdType 
                     data:(NSString *)responseData 
               length:(NSInteger)length {
    NSLog(@"didReceiveMessage");
    // Do something with data and cmdType...
    [joinLock lock];
    [joinLock unlockWithCondition:YES];
    callBackInvoked = YES;
}

NFCThread のメイン メソッドをオーバーロードして、継続的にループするようにしました。このようなもの:

while (!callBackInvoked) { ; }

これは、CPU の使用率が非常に高くなるため、あまり良い考えではないことがわかりました。代わりに、このサイトで見つけたいくつかの例から実行ループを使用してみました。

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

while (!callBackInvoked) {
    [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

私の実装の両方で、メイン スレッドは常にブロックされており、どのデリゲート メソッドも呼び出されていないように見えます。ただし、ライブラリが正常に機能していることと、デリゲート メソッドへの呼び出しが正常に呼び出されることはわかっています。

ここで明らかな何かが欠けているように感じます。どんな助けでも大歓迎です。

リッチ

4

4 に答える 4

4

非同期コールバックがセマフォに信号を送って続行できるようになるまで、プライマリコードパスをブロックできるようにするセマフォが必要です。

セマフォは、Grand Central Dispatch を通じて iOS 4 で利用できます。

iOS 3 では NSCondition を使用してセマフォの動作を実装できるようです。

于 2010-08-05T23:10:32.480 に答える
3

似たようなものを実装しました。コンテキストは次のとおりです。 - バックグラウンド スレッドで ZipArchive への複数の呼び出しをラップする - 解凍する呼び出しごとに、異なる進行状況メーターを表示します (ファイルの名前が展開された無限に回転するホイール) - すべてのアーカイブが終了したら、クリーンアップ タスクを実行します拡張された

NSConditionLock を使用すると簡単になり、コードは次のように機能します。

NSConditionLock* lock = alloc/init
for(int idx = 0; idx < [list count]; idx++) {
  NSString* fileName = ["getIdx's element in list"]
  [cond lockWhenCondition:idx]; //

  ... prepare things before forking

  [cond unlockWithCondition:-1];
  [Notification forkBlock:^(void){
    [cond lockWhenCondition:-1];

    NSAutoreleasePool *pool = [alloc/init];
    [ZipArchive unzipFile:fileName];
    [pool release];
    [cond unlockWithCondition:idx+1];
  }];
}

[cond lockWhenCondition:[list count];
// all the tasks are now completed

各ブロックは、バックグラウンド スレッドでスケジュールされます。Notification クラスは、糸車を使用して UIView をアニメーション化し、ブロックを別のスレッドにラップします。ロック/ロック解除は同じスレッドから呼び出される必要があるため、条件は、元のスレッドとバックグラウンド スレッド間のピンポンを有効にするものです (バックグラウンドの場合は -1、フォアグラウンドの場合は 1,2,3..n)。

于 2011-10-07T23:13:22.590 に答える
0

やりたいことは、NSCondition を使用して現在のスレッドを待機し、2 番目のスレッドで最初のスレッドを続行させるように通知することです。

- (void) firstThread
{
    workIsDone = NO;
    //Start second thread here
    [condition lock];
    while (!workIsDone) {
        [condition wait];
    }
    [condition unlock];

    // Keep going
}

- (void) secondThread
{
    [condition lock];

    //Do some work.
    workIsDone = YES;
    [condition signal];
    [condition unlock];
}
于 2011-12-16T15:28:51.593 に答える
0

わかりました、ここにはいくつかの異なる問題があります。私はどこから始めるべきかを考えようとしています.

しかし、あなたが何を達成しようとしているのかを理解するために、呼び出しを同期的に「表示」したいと言った場合、呼び出しをブロックしたいということですか? メインスレッドからこの呼び出しを行っていますか? もしそうなら、あなたは意図的にメインスレッドをブロックしているようです。

サードパーティのライブラリは、おそらくメインの実行ループでイベントをスケジュールしていることに注意してください。独自の実行ループを作成して別のスレッドで実行できますが、そのイベントにその実行ループを使用するように他のライブラリに指示しましたか? (たとえば、ブロックしたメインの実行ループでスケジュールされている非同期ネットワーク要求を作成している可能性があります)

あなたが何をしているのかを少し考え直しますが、最初に、この呼び出しを行っているスレッドをブロックすることが意図されているかどうかを知る必要があります. また、iPhoneOS 3.x をサポートする必要がありますか?

于 2010-08-05T23:44:11.523 に答える