How to avoid nested blocks

雨燕双飞 提交于 2020-01-16 06:39:32

问题


GCD and blocks are so nice and convenient. But when I fall in love with it, I found that something bad happened. Look at these codes below:

[self functionA:^(BOOL success) {
  if (success) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
      [self functionB:^(NSError *error) {
        if (error != nil) {
          dispatch_async(dispatch_get_main_queue(), ^(void) {
            [self functionC:^(id result) {
              if (result) {
                [self functionD:^(BOOL success) {
                  if (success) {
                    [self DoSomething];
                  }
                }];
              }
            }];
          });
        }
      }];
    });
  }
}];

Crazy? Yes. I am in this trouble.

Did someone have any experience avoiding nested blocks like this?

Edited:

Thanks guys. Exactly, we have more elegant ways to do this. Such as:

  • Declare blocks beforehand
  • Make sub blocks as independent function

But what I expect is a general solution. Maybe like this:(Pseudo code below)

functionA.flat.success = [self functionB];
functionB.flat.isntnil = [self functionC];
functionB.flat.error = {};
functionC.flat.isntnil = [self functionD];
[flat call:functionA];

回答1:


Well, I haven't bothered matching your cloud of closing braces, but here's a try by simply using return, which you can use freely inside blocks too and cuts the nesting a little:

[self functionA:^(BOOL success) {
  if (!success)
    return;

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
    [self functionB:^(NSError *error) {
      if (!error)
        return;

      dispatch_async(dispatch_get_main_queue(), ^(void) {
        [self functionC:^(id result) {
          if (!result)
            return;

          [self functionD:^(BOOL success) {
            if (!success)
              return;

            [self DoSomething];
          }];
        }];
      });
    }];
  });
}];

Also, nobody forces you to write blocks inline, you can declare them as normal variables before and use them later. In fact, by declaring blocks before you are able to reuse them if your API is lenient towards its users and allows being called repeatedly even when no work has to be done:

- (void)foo:(Bar*)bar
{
    // Prepare the success handler.
    void (^successBlock)(Bar*) = ^(Bar *bar) {

        [[NSNotificationCenter defaultCenter]
            postNotificationName:@"barUpdated"
                          object:bar];
    };

    if (!bar.didAlreadyFetchStuff) {
        [self wellYouBetterFetchSomething:bar withSuccess:successBlock];
    } else {
        // Oh, fake we already did the work.
        successBlock(bar);
    }
}

Whenever I see the nest level too high I put the inside blocks as normal methods in the class and simply call them inside the block. The effect is the same, but it looks much cleaner, and it allows you to use appledoc or other documentation tools for each method rather than hoping to understand the mess of nested undocumented blocks.

It only gets crazy if you allow it to get crazy.



来源:https://stackoverflow.com/questions/18311840/how-to-avoid-nested-blocks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!