Asynchronous programming in Objective-C: I feel like there is repeating code here, and I don't know what I can do about it

I have been working with asynchronous programming for a while, and I think I understand the concepts, but there is a certain scenario that I feel I don’t get. Check the code:

-(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);
        }
    }];
}

Basically, the handler should be called as soon as there is either an error, or both async operations return successfully. I feel like there is duplicate code here, and I don't know what I can do about it. I cannot unconditionally call the handler () in the asyncMethod1 success block, because for case # 3, it needs an asynchronous method to succeed or a failure to call it.

- , ? , .

+4
6

, , , handler. , . . , , - .

, asyncMethod1 2 , , , 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);
    }];
}
+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);
        }];
    }
}
+1

, , :

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 eventual result value:
    if (result != nil) {
        handler(1);
    }
    return nil;
}, ^id(NSError* error) {
    handler(2);
    return nil;
});

, Promises ( Objective-C, ), , , , .

RXPromise, Objective-C, Promises Promises/A + .

0

EDIT , , , ?

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... . , , , , .

0

?

-(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);
    }];
}
0

, . if-else .

:

  • . . (, .)
  • , nil , .
  • , if-else .
  • , if-else. - NSDictionary .

Here is the code with the attached tips:

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

    if ( ! handler) handler = ^(int result) {}; // Use empty block, so it 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);
    }];
}

I'm not sure how many result codes you have. If you only indicate success / failure, it might be easier.

0
source

All Articles