问题
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