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() 因为 case #3 需要它的异步方法成功或失败才能被调用。

任何人都可以建议一种模式来帮助这里吗?在使用像这样的嵌套异步操作时,我感到最不舒服。

4

6 回答 6

1

这不是一个真正的答案,但它是重新格式化您的代码以使其更简单一些。它可能不起作用(取决于是什么//some code)。首先,我在您的代码中看到的是,如果处理程序无效,它什么也不做(这是//some code更改我的响应的地方。如果您将 return 放入唯一不调用的 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 回答
1

我所做的最大更改是定义了一个块变量,该变量负责多次检查是否handler存在。为了减轻您对嵌套异步方法的不安,我刚刚定义了一个新方法并调用了它。其余的更改只是格式化。最后,我认为这仍然提供相同的功能,并且更紧凑且更易于遵循。

我不确定asyncMethod1and2是否是对象,所以我只是假设它们是在同一个类中定义并插入的方法self

- (void)callAsyncMethod2WithHandler:(void (^)(int result))handler {
    [self asyncMethod2Success:^(NSDictionary *dict) {
        handler(1);
    } failure:^(NSError *error) {
        handler(2);
    }];
}

- (void)someMethod:(void (^)(int result))handler {
    void (^safeHandler)(int) = ^void (int theResult) {
        if (handler) handler(theResult);
    };

    [self asyncMethod1Success:^(NSDictionary *dict) {
        NSString *someValue = dict[@"someKey"];
        if ([someValue isEqualToString:kString1]) {
            // some code
            safeHandler(1);
        } else if ([someValue isEqualToString:kString2]) {
            safeHandler(1);
        } else if ([someValue isEqualToString:kString3]) {
            [self callAsyncMethod2WithHandler:safeHandler];
        }
    } failure:^(NSError *error) {
        safeHandler(2);
    }];
}
于 2013-10-18T18:56:49.473 回答
0

How about using blocks?

-(void)someMethod:completionHandler:(void (^)(int result))handler
{
    void (^safeHandler)(int) = ^(int value) {
        if (handler)
        {
            handler(value);
        }
    };

    [anotherObject asyncMethod1Success:^(NSDictionary *dict)
    {
        if ([dict[@"someKey"] isEqualToString:kString1])
        {
            // some code

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

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

            [anotherObject asyncMethod2Success:^(NSDictionary *dict)
            {
                safeHandler(1);
            }
            failure:^(NSError *error)
            {
                safeHandler(2);
            }];
        }
    }
    failure:^(NSError *error)
    {
        safeHandler(2);
    }];
}
于 2013-10-18T21:15:30.367 回答
0

我认为您的重复示例不仅与该方法的异步方面有关。我也会专注于从if-else分支中删除重复项。

减少代码的提示:

  • 不要创建带有成功和失败块的方法。使用一个块来报告两种情况。(是的,有时你不能只改变现有的方法。)
  • 检查块是否nil只有一次,并在这种情况下使用一些默认的空块。
  • 提取它们之外的所有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 回答
0

这似乎是一个人为的例子,但如果您不介意使用小型第三方库,您的代码可以模仿如下:

handler_block_t handler = ...;

RXPromise* p0 = someMethod();

p0.then(^id(id dict) {
    if ([dict[@"someKey"] isEqualToString:kString1]) {
        // some code
        return @"OK";
    }
    else if ([dict[@"someKey"] isEqualToString:kString2]) {
        return @"OK";
    }
    else if ([dict[@"someKey"] isEqualToString:kString3]) {
        return asyncMethod2();
    } else {
        return nil; // not handled
    }
}, nil).then(^id(id result /* dict or @"OK" or nil */) {
    // parameter result is either @"OK", nil (not handled), or asyncMethod2's eventual result value:
    if (result != nil) {
        handler(1);
    }
    return nil;
}, ^id(NSError* error) {
    handler(2);
    return nil;
});

当然,如果您使用 Promises(Objective-C 中有一些实现它的库),您可能会重组代码以使其更易于理解。

我是RXPromise的作者,这是一个 Objective-C 库,它根据Promises/A+ Specification实现了 Promises 的概念。

于 2013-10-18T18:56:18.110 回答
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 回答