0

As apple's documentation says,

"Blocks are also used for callbacks, defining the code to be executed when a task completes."

So block should execute after the body of the function in which the block is passed executes. But I wrote the following test code:

void testBlock(void(^test)()){
    NSLog(@"1");
};

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        testBlock(^{
            NSLog(@"2");
        });
    }
}

and the output is only "1". So where's the NSLog(@"2")?

@Julian Król But look at this function in AFNetworking:

- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                          failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
    if (self.completionGroup) {
        dispatch_group_enter(self.completionGroup);
    }

    dispatch_async(http_request_operation_processing_queue(), ^{
        if (self.error) {
            if (failure) {
                dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                    failure(self, self.error);
                });
            }
        } else {
            id responseObject = self.responseObject;
            if (self.error) {
                if (failure) {
                    dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                        failure(self, self.error);
                    });
                }
            } else {
                if (success) {
                    dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{
                        success(self, responseObject);
                    });
                }
            }
        }

        if (self.completionGroup) {
            dispatch_group_leave(self.completionGroup);
        }
    });
};
#pragma clang diagnostic pop
}

This function doesn't explicitly call the block and the block parameter doesn't even has a name so it seems that the block should not be executed. But as I use this function as following:

AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFXMLParserResponseSerializer serializer];
operation.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"application/rss+xml"];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {

    NSXMLParser *XMLParser = (NSXMLParser *)responseObject;
    [self.parserDictionary setObject:XMLParser forKey:urlString];
    [XMLParser setShouldProcessNamespaces:YES];
    XMLParser.delegate = self;
    [XMLParser parse];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    //When an error occurs while parsing.
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Loading Data"
                                                        message:[error localizedDescription]
                                                       delegate:nil
                                              cancelButtonTitle:@"Ok"
                                              otherButtonTitles:nil];
    [alertView show];
    [MBProgressHUD hideHUDForView:self.tableView animated:YES];

}];

And the block is truly executed. What's is the reason here?

Julian
  • 9,299
  • 5
  • 48
  • 65
Leo
  • 492
  • 1
  • 5
  • 15
  • the problem with blocks you have is that you are mixing when you are passing a block and the function calling that block. In the example from afnetworking you see the implementation of a function for which you implement the block that it will execute inside. You would have to look at those methods implementation details to see where those blogs are called. – Julian Aug 17 '15 at 11:44
  • I read the article and I can tell the function and the block apart but here in this AFNetworking function the passed in block doesn't even has a name, how can the function call the block? – Leo Aug 17 '15 at 12:47
  • the function in the definition gives a name to each attribute so to know how to refer to this :) – Julian Aug 17 '15 at 13:38
  • Ah so in the parameter list, success and failure are the names of block? I only know I can name a block variable with `void (^success)(AFHTTPRequestOperation *operation, id responseObject)`... – Leo Aug 17 '15 at 14:04
  • yes success :) when there is a method signature in the implementation file it has a name for the attribute to have the way to reference to the attribute in the function body – Julian Aug 17 '15 at 14:23
  • In short method getting block as an attribute decides whether and when to call a passed block – Julian Aug 17 '15 at 14:36
  • Okay thx I get it :) – Leo Aug 17 '15 at 15:20

2 Answers2

1

you do not call passed block inside this is why you have on the output only NSLog(@"2");

You should have something like this:

void testBlock(void(^test)()){
    NSLog(@"1");
    test();
};

You should also check whether passed block is a nil (as calling it in case it is nil will crash)

Julian
  • 9,299
  • 5
  • 48
  • 65
  • Ah so the executive order of block is set by me, not necessarily executed after the body of the function? – Leo Aug 17 '15 at 11:21
  • If you place the block before the log this is how it will be executed. Yes you are defining the order as long as you do it synchronously. In the example I posted it will be printed 1 and then 2. – Julian Aug 17 '15 at 11:22
  • Thx but see my newly-edited answer and I'm still puzzled here. – Leo Aug 17 '15 at 11:42
  • http://code.tutsplus.com/tutorials/understanding-objective-c-blocks--mobile-14319 maybe this will helps you more especially second part of the tutorial – Julian Aug 17 '15 at 11:47
0

You can think of blocks as chunks of code that can be passed around like variables. You call a block like a function so in your case you would do something like !test ?: test(); This is semantically equivalent to:

if (test != nil) {
    test();
}

You may find the following example useful:

// Define block that returns void and takes void arguments
typedef void (^MyBlock)(void);
// Define block that takes two ints and returns an int
typedef int (^AddBlock)(int a, int b);

@interface MyClass : NSObject

// NOTE: You have to COPY the block variables.
@property (copy) MyBlock blockOne;
@property (copy) AddBlock blockTwo;

@end

@implementation ...

- (void)runBlocks
{
    !self.blockOne ?: self.blockOne();
    if (self.blockTwo != nil) {
        int sum = self.blockTwo(1, 2);
    }
}

@end

function in AFNetworking:

A couple of things:

1) You should not have a failure and a success block. Instead define one block like this:

typdef void (^CompletionHandler)(AFHTTPRequestOperation *op, id responseObj, NSError *error);

Then when you implement the block you can check for an error like so:

...^(AFHTTPRequestOperation *op, id responseObj, NSError *error) {
    if (error != nil) {
        // Handle the error
    } else {
        // YAY everything went well
    }
}];

2) You are ignoring retain cycles here which is NOT a good thing to do. Instead you should define a weak version of self that you can reference in the block like so:

__weak __block MyClass *welf = self;
myBlock = ^{
   welf.coolProperty = coolValue;
};

Check out this SO question for info on __weak and __block

Community
  • 1
  • 1
Rob Sanders
  • 5,197
  • 3
  • 31
  • 58
  • Thx but I want to know the executive order of block. – Leo Aug 17 '15 at 11:26
  • Do you mean the order in which the blocks are called? That all depends on how you call them and if you're using concurrency. e.g. `test(); NSLog(@"1");` will produce `"2,1"` and `NSLog(@"1"); test();` will produce `"1,2"` – Rob Sanders Aug 17 '15 at 13:16
  • Exactly the order. Yeah now I understand this and please look at my newly-edited question. – Leo Aug 17 '15 at 13:28
  • @Ceasar I edited my answer to include some things about the code in your edited answer. I hope you've got what you need now. – Rob Sanders Aug 17 '15 at 21:22
  • "You are ignoring retain cycles here which is NOT a good thing to do." No he is not. What makes you think there may be a retain cycle here? – newacct Aug 17 '15 at 23:24
  • `#pragma clang diagnostic ignored "-Warc-retain-cycles"` and the fact that he is referencing `self` in a block that is owned by `self` which means that `self` owns `self`. Standard ingredients for a retain cycle. – Rob Sanders Aug 18 '15 at 08:04
  • You mean using typedef then use `CompletionHandler success, failure;` instead to simplify the code? Thx and I'll notice the second one. – Leo Aug 18 '15 at 12:55
  • @Caesar not necessarily `typedef` but you should have only one block with an error argument that you check first. – Rob Sanders Aug 18 '15 at 14:20
  • Not completely understood for the method `setCompletionBlockWithSuccess:Failure:` truly needs 2 blocks which has same type of arguments. – Leo Aug 19 '15 at 09:28