4

私はしばらく非同期プログラミングに取り組んできましたが、概念は理解していると思いますが、得られていないように感じる特定のシナリオがあります。コードをチェックしてください:

-(void)someMethod:completionHandler:(void (^)(int result))handler
{
    [anotherObject asyncMethod1Success:^(NSDictionary *dict)
    {
        if ([dict[@"someKey"] isEqualToString:kString1])
        {
            // some code

            if (handler)
            {
                handler(1);
            }
        }
        else if ([dict[@"someKey"] isEqualToString:kString2])
        {
            // some different code

            if (handler)
            {
                handler(1);
            }
        }
        else if ([dict[@"someKey"] isEqualToString:kString3])
        {
            // even different code

            [anotherObject asyncMethod2Success:^(NSDictionary *dict)
            {
                if (handler)
                {
                    handler(1);
                }
            }
            failure:^(NSError *error)
            {
                if (handler)
                {
                    handler(2);
                }
            }];
        }
    }
    failure:^(NSError *error)
    {
        if (handler)
        {
            handler(2);
        }
    }];
}

基本的に、ハンドラーは、エラーが発生した場合、または両方の非同期操作が正常に返された場合に呼び出される必要があります。ここには反復的なコードがあるように感じますが、それに対して何ができるかわかりません。asyncMethod1 の成功ブロックで無条件に handler() を呼び出すことはできません。これは、ケース #3 では非同期メソッドが呼び出されるために成功または失敗する必要があるためです。

ここで役立つパターンを提案できる人はいますか? このようなネストされた非同期操作を操作するとき、私は最も不快に感じます。

4

6 に答える 6

1

これは実際には答えではありませんが、コードを少し単純にするためにコードを再フォーマットしています。動作しない場合があります(内容によって異なり//some codeます)。最初に私があなたのコードで見たのは、ハンドラーが有効でない場合は何もしないということです(これが//some code私の応答を変更する場所です。呼び出しを行わない唯一のif/thenケースにリターンを入れると、handler(1)で呼び出すことができます関数の終わり。

-(void)someMethod:completionHandler:(void (^)(int result))handler
{
    if(handler)
    {
        [asyncMethod1 success:^(NSDictionary *dict)
        {
            NSString *test = dict[@"someKey"];
            if (test isEqualToString:kString1])
            {
                // some code
            }
            else if (test isEqualToString:kString2])
            {

            }
            else if (test isEqualToString:kString3])
            {
                [asyncMethod2 success:^(NSDictionary *dict)
                 {
                    handler(1);
                }
                failure:^(NSError *error)
                {
                    handler(2);
                }];
                return;
            }
            handler(1);
        }
        failure:^(NSError *error)
        {
            handler(2);
        }];
    }
}
于 2013-10-18T18:49:04.087 に答える
0

編集元のコードに近い例を次に示します。おそらく、この配置が好きですか?

enum CompletionResult { Fail, Success };

@interface Test : NSObject
@property ( nonatomic, readonly ) dispatch_queue_t q ;
@end

@implementation Test

-(dispatch_queue_t)q
{
    return dispatch_get_main_queue() ;
}

-(void)method1WithSuccessBlock:(void(^)(NSDictionary * dict))successBlock failureBlock:(void(^)(void))failBlock
{
    // long running method 1
    // return YES/NO on success/failure
}

-(void)method2WithSuccessBlock:(void(^)(NSDictionary * dict))successBlock failureBlock:(void(^)(void))failBlock
{
    // long running method 2
    // return YES/NO on success/failure
}

-(void)test:(void(^)(enum CompletionResult))block
{
    __block BOOL performAsync2 = NO ;
    void (^successBlock)(NSDictionary * dict) = ^(NSDictionary * dict){
        NSString * value = dict[@"someKey"] ;
        if ( [ value isEqualToString:A ] )
        {

        }
        else if ( [ value isEqualToString:B ])
        {

        }
        else if ( [ value isEqualToString:C ] )
        {
            performAsync2 = YES ;
        }

        if (  !performAsync2 && block )
        {
            block( Success ) ;
        }

    };
    void (^failBlock)() = ^{
        if ( block ) { block( Fail ) ; }
    };

    dispatch_sync( self.q, ^{ [ self method1WithSuccessBlock:successBlock failureBlock:failBlock ] } ) ;
    if ( performAsync2 )
    {
        dispatch_sync( self.q, ^{ [ self method2WithSuccessBlock:successBlock failureBlock:failBlock ] } ) ;
    }
}

@end

このようなことができますか?

@interface Test : NSObject
@property ( nonatomic, readonly ) dispatch_queue_t q ;
@end
@implementation Test

-(dispatch_queue_t)q
{
    return dispatch_get_main_queue() ;
}
-(BOOL)method1
{
    // long running method 1
    // return YES/NO on success/failure
}

-(BOOL)method2
{
    // long running method 2
    // return YES/NO on success/failure
}

-(void)test:(void(^)(enum CompletionResult))block
{
    __block BOOL didSucceed = NO ;
    dispatch_sync( self.q, ^{ didSucceed = [ self method1 ] ; }) ;
    if ( !didSucceed )
    {
        if ( block ) { block( Fail ) ; }
        return ;
    }
    dispatch_sync( self.q, ^{ didSucceed = [ self method2 ] ; }) ;
    if ( !didSucceed )
    {
        if ( block ) { block( Fail ) ; }
        return ;
    }

    if ( block ) { block( Success ) ; }
}

@end

async1 と async2 の呼び出し方法を変更する必要がありますが、メイン コードは簡単に理解できます。もちろん、使用するキューがバックグラウンドで実行されていることを確認してください。

于 2013-10-18T20:52:09.273 に答える
0

あなたの繰り返しの例は、そのメソッドの非同期の側面だけに関連しているとは思いません。if-elseブランチからの重複の削除にも焦点を当てます。

コードを減らすためのヒント:

  • 成功と失敗のブロックを取るメソッドを作成しないでください。1 つのブロックを使用して、両方のケースを報告します。(はい、既存のメソッドを変更できない場合もあります。)
  • ブロックがnil1 回だけであることを確認し、その場合はデフォルトの空のブロックを使用します。
  • それら以外のすべてのif-elseブランチに共通するコードを抽出します。
  • 可能であれば、if-elseまったく実行しないでください。ある種のマッピングNSDictionaryまたは他のアプローチを使用する方が簡単な場合があります。

ヒントを適用したコードは次のとおりです。

- (void)someMethodWithCompletionHandler:(void (^)(int result))handler {

    if ( ! handler) handler = ^(int result) {}; // Use empty block, so it's never nil.

    [anotherObject asyncMethod1Success:^(NSDictionary *dict) {

        NSString *someString = dict[@"someKey"];
        int result = 0; // Default

        if ([someString isEqualToString:kString1]) {
            // some code
            result = 1;
        }
        else if ([someString isEqualToString:kString2]) {
            // some different code
            result = 1;
        }
        // ... maybe even more branches
        else if ([someString isEqualToString:kString3]) {
            // even different code

            [anotherObject asyncMethod2Success:^(NSDictionary *dict) {
                handler(1);
            }
            failure:^(NSError *error) {
                handler(2);
            }];
        }

        if (result) handler(result);
    }
    failure:^(NSError *error) {
        handler(2);
    }];
}

結果コードがいくつあるかわかりません。成功/失敗のみを示す場合は、より簡単なアプローチがあるかもしれません。

于 2013-10-18T21:26:54.333 に答える