2

より再利用可能なコードを作成できるように改善しようとしています。現在、アプリには、データベースへのすべての呼び出しが通過する DataManager シングルトンがあります。したがって、データベースへの高価なルックアップのために、その呼び出しをネストされた dispatch_async ブロックに入れて、メイン スレッドをブロックしないようにしたいと考えています。ですから現在、

ViewControllerA で:

dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(aQueue, ^{
  NSArray *array = [DataManager myExpensiveMethodCall];
  dispatch_async(dispatch_get_main_queue(), ^{
    [self setEvents:array];
  });
});

このタイプの「パターン」をより適切に抽象化する方法があるかどうか疑問に思っていました。Apple のサンプル コードで見たことがあります。私は基本的に、高価なメソッドを実行してから UI を更新する必要があるときはいつでも、このタイプのネストされた dispatch_async 呼び出しを実行します。この種のことを行う必要があるViewControllerにこの種のコードを入れるだけで「大丈夫」なのか、それとももっと良い方法があるのか​​ 知りたかったのです。ありがとう。

4

3 に答える 3

3

.hファイルでこれを@interfaceの外に置きます

typedef void(^MyBlockType)(void);

.mファイルでは、次のようなものを使用できます

+(void)doAsyncWithBlock:(MyBlockType)asyncBlock andSyncBlock:(MyBlockType)syncBlock
{
    dispatch_queue_t aQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(aQueue, ^{
        if( asyncBlock != nil )
        {
            asyncBlock();
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if( syncBlock != nil )
            {
                syncBlock();
            }
        });
    });
}
于 2013-02-12T03:14:07.680 に答える
0

はい、これは可能です。主な利点は、GCD への依存を取り除き、コードの大部分を主要な同時実行テクノロジにとらわれないようにできることです。

これをView Controllerクラスに追加するのではなく、非同期作業ユニットのディスパッチを管理し、それらのコールバックを調整する責任を持つ新しいクラスを開始するのが合理的だと思います。このようなオブジェクトは、後でカスタム ディスパッチ キューまたは を維持しNSOperationQueueたり、将来のテクノロジが登場したりする可能性があります。私は、基礎となるスレッド技術に依存しないブロックをディスパッチするための API も提供するデフォルトのインスタンスを持つものを想像しています。GCD を使用した例は次のとおりです。

typedef void(^AsynchronousWorkManagerBlock)();

@interface AsynchronousWorkManager : NSObject

+ (AsynchronousWorkManager *)defaultManager;

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock;

@end


@implementation AsynchronousWorkManager

+ (AsynchronousWorkManager *)defaultManager
{
    static dispatch_once_t onceToken;
    static AsynchronousWorkManager *DefaultManager = nil;
    dispatch_once(&onceToken, ^{
        DefaultManager = [[self alloc] init];
    });
    return DefaultManager;
}

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        backgroundBlock();
        dispatch_async(dispatch_get_main_queue(), callbackBlock);
    });
}

@end

クライアントコードは次のように呼び出すことができます:

[[AsynchronousWorkManager defaultManager] executeInBackground:^{
                                            NSLog(@"This code is happening in the background");
                                        }
                                      withMainThreadCallback:^{
                                            NSLog(@"This code is happening on the main thread");
                                        }];

[[AsynchronousWorkManager defaultManager] executeInBackground:^{
                                            sleep(3);
                                        }
                                      withMainThreadCallback:^{
                                          NSLog(@"Done Sleeping");
                                      }];

後でNSOperationQueueGCD の代わりに使用することに切り替えた場合、次の実装例のように、API を変更せずに簡単に変更できます。

@interface AsynchronousWorkManager ()

@property (nonatomic, strong) NSOperationQueue *operationQueue;

@end


@implementation AsynchronousWorkManager

+ (AsynchronousWorkManager *)defaultManager
{
    static dispatch_once_t onceToken;
    static AsynchronousWorkManager *DefaultManager = nil;
    dispatch_once(&onceToken, ^{
        DefaultManager = [[self alloc] init];
    });
    return DefaultManager;
}

- (id)init
{
    if (self = [super init]) {
        _operationQueue = [[NSOperationQueue alloc] init];
    }

    return self;
}

- (void)executeInBackground:(AsynchronousWorkManagerBlock)backgroundBlock
     withMainThreadCallback:(AsynchronousWorkManagerBlock)callbackBlock
{
    NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:backgroundBlock];
    blockOperation.completionBlock = ^{ [[NSOperationQueue mainQueue] addOperationWithBlock:callbackBlock]; };

    [self.operationQueue addOperation:blockOperation];
}

@end

これがどれほど必要かは完全にはわかりませんが、多くのコードがなくても達成できるので、オーバーエンジニアリングの練習だけではなく、おそらく価値があると判断します. あなたのアプリケーションがさまざまなプライオリティ キューやより特化した GCD 機能を広範囲に使用している場合、根本的な実装の柔軟性を真に得ずに漏れやすい抽象化を導入する可能性があるため、これに移行することには慎重です。

于 2013-02-12T04:21:13.567 に答える
0

通常、これら 2 つの関数を定義します。それらはコードをよりコンパクトにし、メイン スレッドをブロックしたくない場合に使用するパターンに従います (したがって RUN_ON_BACKGROUND_THREAD 呼び出しは非同期です) が、メイン スレッドを更新するためにバックグラウンド スレッドをブロックしても問題ありません (したがって、 RUN_ON_UI_THREAD) 呼び出しは同期的です。また、メイン スレッドから呼び出された場合に呼び出しを直接実行できるようにすることで、デッドロックを回避します。

void RUN_ON_UI_THREAD(dispatch_block_t block)
{
    if ([NSThread isMainThread])
        block();
    else
        dispatch_sync(dispatch_get_main_queue(), block);
}

void RUN_ON_BACKGROUND_THREAD(dispatch_block_t block)
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
}
于 2013-02-12T04:54:21.137 に答える