150

How can I pass a Block to a Function/Method?

I tried - (void)someFunc:(__Block)someBlock with no avail.

ie. What is the type for a Block?

Jacksonkr
  • 31,583
  • 39
  • 180
  • 284

11 Answers11

264

The type of a block varies depending on its arguments and its return type. In the general case, block types are declared the same way function pointer types are, but replacing the * with a ^. One way to pass a block to a method is as follows:

- (void)iterateWidgets:(void (^)(id, int))iteratorBlock;

But as you can see, that's messy. You can instead use a typedef to make block types cleaner:

typedef void (^ IteratorBlock)(id, int);

And then pass that block to a method like so:

- (void)iterateWidgets:(IteratorBlock)iteratorBlock;
Jonathan Grynspan
  • 43,286
  • 8
  • 74
  • 104
  • Why are you passing id as an argument? Is it not possible to easily pass an NSNumber for example? How would that look? – bas Apr 23 '12 at 17:10
  • 7
    You can certainly pass a strongly-typed argument such as `NSNumber *` or `std::string&` or anything else you could pass as a function argument. This is just an example. (For a block that's equivalent except for replacing `id` with `NSNumber`, the `typedef` would be `typedef void (^ IteratorWithNumberBlock)(NSNumber *, int);`.) – Jonathan Grynspan Apr 24 '12 at 00:50
  • This shows method declaration. One trouble with blocks is that the "messy" declaration style does not make it clear and easy to write the actual method call with a real block argument. – uchuugaka Feb 18 '13 at 22:56
  • Typedefs not only make the code easier to write, but significantly easier to read since block/function pointer syntax isn't the cleanest. – pyj Oct 18 '14 at 15:12
  • @JonathanGrynspan, coming from the Swift world but having to touch on some old Objective-C code, how can I tell if a block is escaping or not? I read that by default, blocks are escaping except if decorated with `NS_NOESCAPE`, but `enumerateObjectsUsingBlock` I'm told is non-escaping, yet I don't see `NS_NOESCAPE` anywhere in site, nor is escaping mentioned at all in the Apple docs. Can you help? – Mark A. Donohoe Jul 23 '20 at 04:56
  • How can this be the accepted answer when it doesn't show how to pass a block as a parameter to a method? All the answers here show how to declare block parameters, but no one actually ever uses them. Weird. – Elise van Looij Mar 02 '21 at 19:54
68

The easiest explanation for this question is follow these templates:

1. Block as a method parameter

Template

- (void)aMethodWithBlock:(returnType (^)(parameters))blockName {
        // your code
}

Example

-(void) saveWithCompletionBlock: (void (^)(NSArray *elements, NSError *error))completionBlock{
        // your code
}

Other use of cases:

2. Block as a Property

Template

@property (nonatomic, copy) returnType (^blockName)(parameters);

Example

@property (nonatomic,copy)void (^completionBlock)(NSArray *array, NSError *error);

3. Block as a method argument

Template

[anObject aMethodWithBlock: ^returnType (parameters) {
    // your code
}];

Example

[self saveWithCompletionBlock:^(NSArray *array, NSError *error) {
    // your code
}];

4. Block as a local variable

Template

returnType (^blockName)(parameters) = ^returnType(parameters) {
    // your code
};

Example

void (^completionBlock) (NSArray *array, NSError *error) = ^void(NSArray *array, NSError *error){
    // your code
};

5. Block as a typedef

Template

typedef returnType (^typeName)(parameters);

typeName blockName = ^(parameters) {
    // your code
}

Example

typedef void(^completionBlock)(NSArray *array, NSError *error);

completionBlock didComplete = ^(NSArray *array, NSError *error){
    // your code
};
EnriMR
  • 3,924
  • 5
  • 39
  • 59
  • 1
    [self saveWithCompletionBlock:^(NSArray *array, NSError *error) { // your code }]; In this example the return type is ignored because it is void? – Alex Sep 04 '19 at 01:02
52

This might be helpful:

- (void)someFunc:(void(^)(void))someBlock;
quaertym
  • 3,917
  • 2
  • 29
  • 41
22

You can do like this, passing block as a block parameter:

//creating a block named "completion" that will take no arguments and will return void
void(^completion)() = ^() {
    NSLog(@"bbb");
};

//creating a block namd "block" that will take a block as argument and will return void
void(^block)(void(^completion)()) = ^(void(^completion)()) {
    NSLog(@"aaa");
    completion();
};

//invoking block "block" with block "completion" as argument
block(completion);
katzenhut
  • 1,742
  • 18
  • 26
Aleksei Minaev
  • 1,488
  • 16
  • 17
9

One more way to pass block using с functions in example below. I`ve created functions to perform anything in background and on main queue.

blocks.h file

void performInBackground(void(^block)(void));
void performOnMainQueue(void(^block)(void));

blocks.m file

#import "blocks.h"

void performInBackground(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void performOnMainQueue(void(^block)(void)) {
    if (nil == block) {
        return;
    }

    dispatch_async(dispatch_get_main_queue(), block);
}

Than import blocks.h when necessary and invoke it:

- (void)loadInBackground {

    performInBackground(^{

        NSLog(@"Loading something in background");
        //loading code

        performOnMainQueue(^{
            //completion hadler code on main queue
        });
    });
}
Dren
  • 2,017
  • 21
  • 18
6

You also can set block as a simple property if it's applicable for you:

@property (nonatomic, copy) void (^didFinishEditingHandler)(float rating, NSString *reviewString);

make sure that block property is "copy"!

and of course you can also use typedef:

typedef void (^SimpleBlock)(id);

@property (nonatomic, copy) SimpleBlock someActionHandler;
iiFreeman
  • 5,165
  • 2
  • 29
  • 42
4

Also you invoke or call a block in using usual c function syntax

-(void)iterateWidgets:(IteratorBlock)iteratorBlock{

    iteratorBlock(someId, someInt);
}

More info on blocks here

http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Blocks/Articles/bxGettingStarted.html#//apple_ref/doc/uid/TP40007502-CH7-SW1

gheese
  • 1,170
  • 1
  • 11
  • 17
4

I always tend to forget about blocks syntax. This always comes to my mind when I need to declare a block. I hope it helps someone :)

http://fuckingblocksyntax.com

Juan Sagasti
  • 326
  • 2
  • 5
  • This saved my time – Le Ding Jun 12 '20 at 03:34
  • A page on "How Do I Declare A Block in Objective-C?" does not answer the question "how to pass a block as a parameter". Seriously, it's rare to see such a total disconnect between a question and answers on SO. – Elise van Looij Mar 02 '21 at 19:57
2

I wrote a completionBlock for a class which will return the values of dice after they have been shaken:

  1. Define typedef with returnType (.h above @interface declaration)

    typedef void (^CompleteDiceRolling)(NSInteger diceValue);
    
  2. Define a @property for the block (.h)

    @property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
    
  3. Define a method with finishBlock (.h)

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
    
  4. Insert previous defined method in .m file and commit finishBlock to @property defined before

    - (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
        self.completeDiceRolling = finishBlock;
    }
    
  5. To trigger completionBlock pass predefined variableType to it (Don't forget to check whether the completionBlock exists)

    if( self.completeDiceRolling ){
        self.completeDiceRolling(self.dieValue);
    }
    
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284
Alex Cio
  • 6,014
  • 5
  • 44
  • 74
1

Despite the answers given on this thread, I really struggled to write a function which would take a Block as a function - and with a parameter. Eventually, here's the solution I came up with.

I wanted to write a generic function, loadJSONthread, which would take the URL of a JSON Web Service, load some JSON data from this URL on a background thread, then return an NSArray* of results back to the calling function.

Basically, I wanted to keep all the background-thread complexity hidden away in a generic reuseable function.

Here's how I would call this function:

NSString* WebServiceURL = @"http://www.inorthwind.com/Service1.svc/getAllCustomers";

[JSONHelper loadJSONthread:WebServiceURL onLoadedData:^(NSArray *results) {

    //  Finished loading the JSON data
    NSLog(@"Loaded %lu rows.", (unsigned long)results.count);

    //  Iterate through our array of Company records, and create/update the records in our SQLite database
    for (NSDictionary *oneCompany in results)
    {
        //  Do something with this Company record (eg store it in our SQLite database)
    }

} ];

...and this is the bit I struggled with: how to declare it, and how to get it to call the Block function once the data was loaded, and pass the Block an NSArray* of records loaded:

+(void)loadJSONthread:(NSString*)urlString onLoadedData:(void (^)(NSArray*))onLoadedData
{
    __block NSArray* results = nil;

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{

        // Call an external function to load the JSON data 
        NSDictionary * dictionary = [JSONHelper loadJSONDataFromURL:urlString];
        results = [dictionary objectForKey:@"Results"];

        dispatch_async(dispatch_get_main_queue(), ^{

            // This code gets run on the main thread when the JSON has loaded
            onLoadedData(results);

        });
    });
}

This StackOverflow question concerns how to call functions, passing a Block as a parameter, so I've simplified the code above, and not included the loadJSONDataFromURL function.

But, if you are interested, you can find a copy of this JSON loading function on this blog: http://mikesknowledgebase.azurewebsites.net/pages/Services/WebServices-Page6.htm

Hope this helps some other XCode developers ! (Don't forget to vote up this question and my answer, if it does !)

Mike Gledhill
  • 27,846
  • 7
  • 149
  • 159
0

The full template looks like

- (void) main {
    //Call
    [self someMethodWithSuccessBlock:^{[self successMethod];}
                    withFailureBlock:^(NSError * error) {[self failureMethod:error];}];
}

//Definition
- (void) someMethodWithSuccessBlock:(void (^) (void))successBlock
                   withFailureBlock:(void (^) (NSError*))failureBlock {
    
    //Execute a block
    successBlock();
    failureBlock([[NSError alloc]init]);
}

- (void) successMethod {
}

- (void) failureMethod:(NSError*) error {
}
yoAlex5
  • 29,217
  • 8
  • 193
  • 205