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
?
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
?
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;
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
};
This might be helpful:
- (void)someFunc:(void(^)(void))someBlock;
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);
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
});
});
}
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;
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
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 :)
I wrote a completionBlock for a class which will return the values of dice after they have been shaken:
Define typedef with returnType (.h
above @interface
declaration)
typedef void (^CompleteDiceRolling)(NSInteger diceValue);
Define a @property
for the block (.h
)
@property (copy, nonatomic) CompleteDiceRolling completeDiceRolling;
Define a method with finishBlock
(.h
)
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock;
Insert previous defined method in .m
file and commit finishBlock
to @property
defined before
- (void)getDiceValueAfterSpin:(void (^)(NSInteger diceValue))finishBlock{
self.completeDiceRolling = finishBlock;
}
To trigger completionBlock
pass predefined variableType to it
(Don't forget to check whether the completionBlock
exists)
if( self.completeDiceRolling ){
self.completeDiceRolling(self.dieValue);
}
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 !)
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 {
}