在Objective-C中避免嵌套块的异步代码

5

我有一长串需要在我的Objective-C代码中发生的事件。假设我有6件事 - thingA,thingB,thingC,thingD,thingE和thingF。thingB和thingD返回一个BOOL值。如果thingB是NO,则不需要调用thingC。如果thingD是NO,则不需要调用thingE。

- (void)doThings:(void(^)())completion {
    [self thingA: ^{
        [self thingB: ^(BOOL success) {
            if (success) {
                [self thingC: ^{
                    [self thingD: ^(BOOL success) {
                        if (thingD) {
                            [self thingE: ^{
                                [self thingF];
                                completion();
                            }];
                            return;
                        }

                        [self thingF];
                        completion();
                    }];
                }];
                return;
            }

            [self thingD: ^(BOOL success) {
                if (success) {
                    [self thingE: ^{
                        [self thingF];
                        completion();
                    }];
                    return;
                }

                [self thingF];
                completion();
            }];
        }];
    }];
}

这可能很快变得难以处理。你可以将那些有不同结果但会重新回到循环中的事情分离出来,并将它们制作成新的方法,如下所示:
- (void)doThings:(void(^)())completion {
    [self thingA: ^{
        [self attemptThingB: ^{
            [self attemptThingD: ^{
                [self thingF];
                completion();
            }]
        }];
    }]
}

- (void)attemptThingB:(void(^)())completion {
    [self thingB: ^(BOOL success) {
        if (success) {
            [self thingC: {
                completion();
            }];
            return;
        }

        completion();
    }];
}

- (void)attemptThingD:(void(^)())completion {
    [self thingD: ^(BOOL success) {
        if (success) {
            [self thingE: ^{
                completion();
            }];
            return;
        }

        completion();
    }];
}

这样可以减少代码的重复,但仍然杂乱无章且难以跟踪。甚至会导致带有尴尬名称的方法,这些方法实际上只是针对特定情况的辅助方法。
一定有更好的方法。一种看起来非常类似于同步编码,但是异步的方法。上面的代码很难阅读,如果我想要添加新内容到流程中,那么它就会变得危险。
有更好的解决方案吗?类似于这样的东西?
- (void)doThings {
    [self thingA];
    BOOL thingBSuccess = [self thingB];
    if (thingBSuccess == YES) {
        [self thingC];
    }
    BOOL thingDSuccess = [self thingD];
    if (thingDSuccess == YES) {
        [self thingE];
    }
    [self thingF];

    return;
}

那个提出的解决方案立刻显露出的问题是,完成处理程序可以传递多个对象,而块的返回值只能处理1个对象。但类似于这样的格式?它更加清晰易懂,易于维护。
1个回答

4
我想你已经发现了"调度组"!
有成千上万的文章介绍它们,所以没有必要在这里无意义地粘贴一些内容。
参考链接:等待多个网络请求完成 - 包括它们的完成回调 祝好!
更简单地说,你可能需要的是"跳出代码",这是编写简洁整洁代码的关键部分。请注意,这不总是解决方案,但通常情况下是如此。同时,我不完全清楚你的问题。但在跳出代码中,你可以这样做...
{
do something;
if ( failure, and you don't want to do any more ) return;
do some other important thing;
if ( failure of that thing, and you don't want to do any more ) return;
do yet another routine here;
if ( some other failed conditions, and you don't want to do any more ) return;
}

下面是一个典型的例子。通常您会看到类似以下代码...
-(void)loadNameInToTheCell
  {
  if ( self.showname != nil && [self.showname notLike:@"blah"] && kount<3 )
        {
        // many many lines of code here
        // many many lines of code here
        // many many lines of code here
        }
  }

你跟上我了吗?但更好的写法是这样的:
-(void)loadNameInToTheCell
  {
  if ( self.showname == nil ) return;
  if ( [self.showname notLike:@"blah"] return;
  if ( kount<3 ) return;

  // many many lines of code here
  // many many lines of code here
  // many many lines of code here
  }

明白了吗?

需要注意的是,在任何实际项目中,关键在于文档而不是代码,这样你才能对其进行适当的讨论和评论...

-(void)loadNameInToTheCell
  {
  if ( self.showname == nil ) return;
  // don't forget Steve's routine could return nil

  if ( [self.showname notLike:@"blah"] return;
  // should we worry about caps here?

  if ( kount<3 ) return;
  // client wants to change this to 4 in future - Steve
  // screw that ... Biff, 8/2014

  // many many lines of code here
  // many many lines of code here
  // many many lines of code here
  }

有道理吧?我希望这些内容可以回答你的问题。你必须反过来思考,你知道吗?
一个可能的经验法则是,如果你有一个条件,然后是一段“非常长的代码块”——这有点不对。把它从另一个角度考虑,设置分离条件,只有在那之后才写上“实际相关的代码”。某种意义上来说,不要在 if 块内放置重要的代码;如果这样做,你的思考方式就有些错误了。

希望这有所帮助,如有任何后续问题,请随时提问。 - Fattie
1
问题在于我的代码大多是异步的。使用调度组进行快速测试后发现它完全不尊重当前线程之外的代码,这使得它对我来说没有用。除非我漏掉了什么? - Andrew
你知道吗,我强烈建议你在新的问题中提供一个“异步调度组”的例子,这样你就不会在当前的问题上得到任何进展了解吗? - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接