Reputation: 967
Is it possible to pass a fully formed block (a block with all its parameters included) into a method to then have that block executed in the method?
At the moment I have this structure repeated in my project:
if (//block exists)
{
if (self.returnOnMainThread)
{
dispatch_async(dispatch_get_main_queue(), ^
{
//call block here
});
}
else
{
//call block here
}
}
However ideally I'd like to abstract the above clunk of code out into a method similar to:
- (void) reportSuccessWithBlock:(GenericBlockType)block{
if (block)
{
if (self.returnOnMainThread)
{
dispatch_async(dispatch_get_main_queue(), ^
{
block;
});
}
else
{
block;
}
}
}
EDIT:
The block's type wouldn't be known in advance.
So the call path could look like this
- (void) someMethod:(void (^)(NSArray *array))success
{
//Some code here setting up the array to be passed back
[self reportSuccessWithBlock:success(array)];
}
There are a few assumptions in the above code (that I don't know are possible):
Upvotes: 1
Views: 14042
Reputation: 122391
OK, I think the idea of using NSArray
parameters will work, so you can give your blocks the same signature:
typedef void ^(MYBLOCK)(NSArray *args);
And implementing your method as:
- (void)reportSuccessWithBlock:(GenericBlockType)block
andArguments:(NSArray *)args
{
if (block != nil)
{
if (self.returnOnMainThread)
{
dispatch_async(dispatch_get_main_queue(), ^{
block(args);
});
}
else
{
block(args);
}
}
}
And then it's just a case of making sure the right type of argument is given to the block in the right order (sounds trivial, but will cause all sorts of aggro if you get this wrong).
MYBLOCK block1 = ^(NSArray *args) {
// I accept NSNumber, NSString, NSValue
NSAssert(args.count == 3, @"Invalid argument count");
NSNumber *arg1 = args[0];
NSString *arg2 = args[1];
NSValue *arg3 = args[2];
// Do my thing
};
and calling it like:
[someClass reportSuccessWithBlock:block1
andArguments:@[ @(1), @"Hello", @(cgpoint) ]];
Upvotes: 1
Reputation: 24481
If you have two different blocks as you said you do in the comments, you can merge the two as follows. However, this is quite hacky, and you will need to check the class of the object
when the block is called.
- (void) reportSuccessWithBlock:(void (^) (id object, NSUInteger value)) block {
if (!block)
return;
if (self.returnOnMainThread) {
dispatch_async(dispatch_get_main_queue(), block((id)someObject, value)); // return 0 instead if there is no value
}
else {
block((id) someObject, value)); // return 0 instead if there is no value
}
}
Therefore, when the block is called, all you would need to do is check the class and do whatever with the returned objects:
- (void) someMethod {
[self reportSuccessWithBlock:^(id object, NSUInteger value) {
if ([object isKindOfClass:[NSArray class]]) {
// returned an NSArray and `value` is 0 (unset)
}
else if ([object isKindOfClass:[NSDictionary class]]) {
// returned an NSDictionary and the `value` is not 0 (unset)
}
else {
// something has gone wrong somewhere!
}
}];
}
Upvotes: 2