2

This is a snippet from AFNetworking's sample code:

+ (void)globalTimelinePostsWithBlock:(void (^)(NSArray *posts, NSError *error))block {
    [[AFAppDotNetAPIClient sharedClient] getPath:@"stream/0/posts/stream/global" parameters:nil success:^(AFHTTPRequestOperation *operation, id JSON) {
        NSArray *postsFromResponse = [JSON valueForKeyPath:@"data"];
        NSMutableArray *mutablePosts = [NSMutableArray arrayWithCapacity:[postsFromResponse count]];
        for (NSDictionary *attributes in postsFromResponse) {
            Post *post = [[Post alloc] initWithAttributes:attributes];
            [mutablePosts addObject:post];
        }

        if (block) {
            block([NSArray arrayWithArray:mutablePosts], nil);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        if (block) {
            block([NSArray array], error);
        }
    }];
}

What I don't understand are:

  1. The (void (^)(NSArray *posts, NSError *error))block part. Assuming that it is a block, does it mean the block is a parameter of the globalTimelinePostsWithBlock method?

  2. Following the first question, can anyone explain the syntax for me? Why is there a block keyword in the end?

hfz
  • 317
  • 2
  • 15

3 Answers3

2

if you don't know how blocks work.. then don't bother trying to understand it just by looking at the code (even if you have used lambda/anonymous functions in other languages like javascript or ruby).. b/c the objective-c syntax is a class on it's own..

i'd recommend you take the time to understand block syntax in obj-c on it's own.. then take a look at examples that use them. This tutorial is excellent (two parts)..

I did what you did before.. and pulled out half my hair.. after looking at the said tutorial.. my hair grew right back up :)


just for fun i'll try to address your specific questions:

1.The (void (^)(NSArray *posts, NSError *error))block part. Assuming that it is a block, does it mean the block is a parameter of the globalTimelinePostsWithBlock method?

yes it is.. so this is a way of calling this method:

// first define the block variable
void(^block)(NSArray *posts, NSError *error) = (NSArray *posts,NSError *error) {

   // block body
   // posts and error would have been passed to this block by the method calling the block.
   // so if you look at the code sample below.. 
   // posts would be [NSArray arrayWithArray:mutablePosts]
   // and error would just be nil
}

// call the function
[AFNetworking globalTimelinePostsWithBlock:block];    

2. Following the first question, can anyone explain the syntax for me? Why is there a block keyword in the end?

basically the block keyword is the name of the argument.. notice how it's used in the body of the method:

if (block) {
            block([NSArray arrayWithArray:mutablePosts], nil);
        }

again to understand how/why.. i recommend you look at the above article.. learning blocks in obj-c has a bit of learning curve.. but once you master it.. it's an amazing tool. please take a look at my answer here to see some sample uses for blocks.

Here is also a sample question/answer that provides a case study of converting delegation into a block based approach, which can also illustrate how blocks work.

Community
  • 1
  • 1
abbood
  • 23,101
  • 16
  • 132
  • 246
  • Thanks for the answer! So I guess what I'm asking next is, if the example from the tutorial is: `(int)(^name)(int)`, where the name is positioned after the return type, why is it that on the AFNetworking above, the name (`block`) is written after the parameter? – hfz Jul 17 '13 at 07:15
  • 1
    they are two different things.. `(int)(^name)(int)` is the name of a variable pointing to a block, whereas `(void (^)(NSArray *posts, NSError *error))block` is an argument *type*, and this argument is referred to as `block`.. it takes a while to sink in.. don't rush it :) now can i have my +1? ;) – abbood Jul 17 '13 at 07:29
  • @hfz if you're still looking into blocks.. check [this](http://stackoverflow.com/questions/17773067/how-to-add-an-extra-argument-to-a-block) out! – abbood Jul 22 '13 at 06:04
1

The block is passed into the method as something to be called when the API call succeeds. globalTimelinePostsWithBlock will call the block passed in with the data (and possibly an NSError)

block in this case isn't a keyword, it's just the name of the variable.

If you wanted to use globalTimelinePostsWithBlock, you would call it like

[ClassName globalTimelinePostsWithBlock:^(NSArray *posts, NSError *error) {
  // Check error, then do something with posts
}];

(where ClassName is the name of the class globalTimelinePostsWithBlock is defined on)

Michael Mior
  • 28,107
  • 9
  • 89
  • 113
1

Block definition are similar to C-functions.

(void (^)(NSArray *posts, NSError *error))block
  • The initial void defines the return type of the function.
  • The ^ is the block pointer. Similar to * for objects.
  • (NSArray *posts, NSError *error) are the parameters with variable names.
  • block is the variable in which this block gets stored. (Bad naming here)
yinkou
  • 5,756
  • 2
  • 24
  • 40
  • 1
    Thanks for answering, especially the `bad naming here` part. It confused me the most and I had a hard time finding why it was used so. – hfz Jul 17 '13 at 07:02