8

結果を取得するためにオンライン サービスを要求するゲッターをオーバーライドしました。ゲッターが同期ブロックからのみ結果を返すように強制するにはどうすればよいですか?

@interface MyClass ()

@property (nonatomic, strong) NSMutableDictionary* myDictionary;
@end 

@implementation MyClass

-(NSMutableDictionary*) myDictionary {

    dispatch_async(queue, ^{
         /* perform online request */
         dispatch_sync(dispatch_get_main_queue(), ^{
             // I need to obtain lock until this line gets executed and only then return
         });
    });
}
@end

少なくとも 3 時間のかなり良いグーグルで、私はdispatch_group_asyncdispatch_semaphoreおよび__blockに出くわしました。使い方が間違っていたのかわかりませんが、目的を果たせませんでした。

更新 1:

myDictionary非同期プロパティです。これがゲッター自体を介して実装できるかどうかを確認したい。

4

3 に答える 3

4

@Kishor リクエストが完全に非同期の場合、UI はブロックされません。「非同期」リクエストがUIをブロックする理由は、実際には非同期ではないためです。疑似コードでの理由は次のとおりです。

- (double) notTruelyAsyncValue 
{
    __block double someExpensiveDoubleToCompute = 0.0;

    // One of the many ways that we can coordinate concurrent threads.
    dispatch_semaphore_t sema_done = dispatch_semaphore_create(0);

    dispatch_async(not_the_main_queue, ^(void) {
       // Simulate long processing time.
       sleep(5); 
       someExpensiveDoubleToCompute = 3.1415926535;
       dispatch_semaphore_signal(sema_done);
    });

    // We can't return until the async block has returned.
    // So we wait until it's done. If we wait on the main queue
    // then our UI will be "frozen". 
    dispatch_semaphore_wait(sema_done, DISPATCH_TIME_FOREVER);

    // Now we have our result we free the resources and return
    dispatch_release(sema_done);

    return someExpensiveDoubleToCompute;
}

このメソッドを非同期スレッドから呼び出すと、UI はブロックされませんが、メイン キュー/スレッドから呼び出すと、メイン スレッドでセマフォを待機しているため、UI がブロックされます。メイン スレッドはシリアル キューであるため、待機の実装方法に関係なく、UI は常にブロックされます。つまり、非同期ブロックが完了するまで、メイン キューの他のブロックやイベントは実行されません。

UI をブロックしたくない場合は、メイン スレッドからブロックする可能性のあるものを呼び出さないでください。これに適したパターンは、@Collin が提案する補完ブロックを使用することです。パターンは次のとおりです。

- (void) computeAnyncValueWithCompletionBlock:((void)^(double value))completionBlock 
{
     dispatch_async(not_the_main_queue, ^(void) {
          // do some expensive computation.
          double value = 3.1415926535;
          completionBlock(value);
     });
}

これはどこからでも呼び出すことができ、ブロックされることはありません。

于 2013-04-19T19:04:30.817 に答える
3

結局のところ、まったく難しいことではありません。私はこれを私の調査結果で更新しようとしましたが、これを更新し続けます。

非同期プロパティのゲッターはどのように動作する必要がありますか?

  1. Perform asynchronous requestプロパティが利用できない場合set the property。(遅延読み込み用)
  2. 利用可能になったらプロパティを返します。

課題:

  • UI がフリーズする
  • 無効な返品

ただし、ここで同期メソッドを使用しない理由のように混乱する可能性があります。

非同期リクエストがいつ完了するかは誰にもわかりませんが、それは、可用性ステータスの必要性全体が不明であることを意味するわけではありません。これには、ハードウェア、カーネルからより高いレベルの API まで、すべてのシステムにメカニズムがあります。これを伝える手段の 1 つとして、プロトコルとデリゲートを参照できます。


非同期プロパティにプロトコルとデリゲートを使用しないのはなぜですか?

  1. 参照するすべてのクラスでデリゲートの実装を強制する必要があります-> Not a getter
  2. 他のクラスに、それが非同期プロパティであることを知られたくありません。データが必要な場合は、データが取得された方法の性質を知らずに、利用可能になったときに取得します。(UIをフリーズせずに明らかに)。

プロトコルとデリゲートを使用せずに、または同期呼び出しに変換せずに、これをどのように達成するのでしょうか?

答えは、条件変数を使用することです。この条件変数は、分岐に使用するものとは異なることに注意してください。スレッド セーフであり、コンパイラとカーネル レベルでサポートされている必要があります。

  1. NSCondition

公式ドキュメントから、

The NSCondition class implements a condition variable whose semantics follow 
those used for POSIX-style conditions. A condition object acts as both a lock 
and a checkpoint in a given thread. The lock protects your code while it tests 
the condition and performs the task triggered by the condition. The checkpoint 
behavior requires that the condition be true before the thread proceeds with its 
task. While the condition is not true, the thread blocks. It remains blocked until 
another thread signals the condition object.

私がしなければならなかったのは、デリゲートを使用せずに、この getter メソッドが非同期要求の完了を認識できるようにすることだけでした。

-(NSMutableDictionary*) myDictionary {
    if(!_myDictionary) {
        _myDicitonary = [self someOtherMethod];
    }
    return _myDictionary;
}

ロックと非同期リクエストは getter 自体に実装できますが、ロックの操作を簡単にするために抵抗しました。また、それはロジックの素晴らしい分離です:)

- (NSMutableDictionary *)someOtherMethod
{
    NSCondition *lockForCompletion = [[NSCondition alloc] init];
    __block BOOL available = NO;
    __block NSMutableDictionary* tempDict = [[NSMutableDictionary alloc] init];

    [lockForCompletion lock]; // acquire the lock

        dispatch_async(queue, ^{
             /* perform online request */
             dispatch_sync(dispatch_get_main_queue(), ^{
                 [tempDict setObject:myResponse forKey:@"mykey" count:1];
                 available = YES;
                 [lockForCompletion signal];
             });
        });
    while(!available) {
        [lockForCompletion wait];
    }

    [lockForCompletion unlock];
    return tempDict;
}

availableまた、最初はブール述語はまったく必要ないように見えることも指摘したいと思いwaitます。しかし、実際にはブール述語は、ドキュメントで説明されているように、ロックを維持する上で非常に重要な役割を果たします。

A boolean predicate is an important part of the semantics of using conditions 
because of the way signaling works. Signaling a condition does not guarantee 
that the condition itself is true. There are timing issues involved in signaling 
that may cause false signals to appear. Using a predicate ensures that these 
spurious signals do not cause you to perform work before it is safe to do so. 
The predicate itself is simply a flag or other variable in your code that you test 
in order to acquire a Boolean result.
于 2013-04-05T04:00:27.860 に答える
3

よくわからない場合は訂正してください。バックグラウンドでダウンロードし、メソッドから非同期ではない方法で戻りたいと思われますか? それについて考えると、2 つの相反することを同時に行おうとしているようなものです。メソッドは、返されるまでブロックするか、非同期で返す必要があります。

あなたが欲しいと思うのは完了ブロックです。をオーバーライドmyDictionaryする代わりに、次のようなことを行う 2 番目のメソッドを作成することもできます。

- (void)downloadWithCompletion:(void(^)(NSDictionary *dictionary))completion
{
    dispatch_async(queue, ^{
         /* perform online request */
         // Create an NSDictionary from what was downloaded.
         NSDictionary *dictionary = <parsed request data>
         dispatch_sync(dispatch_get_main_queue(), ^{
             // Call the completion block using the info that was downloaded
             // where self.myDictionary could be set.
             completion(dictionary);
         });
    });
}
于 2013-04-04T07:43:14.553 に答える