In Objective-C / C, can you write a function that combines 2 blocks?

I often find that I create a wrapper block that simply serves to execute a number of other blocks, usually with the same type signature.

Let's say I have 2 blocks with the same signature:

MyBlockT block1 = ^(NSString *string, id object) { //1 does some work }; MyBlockT block2 = ^(NSString *string, id object) { //2 does some other work }; 

Is there a way to implement the magic function Combine() , which will take 2 blocks:

 MyBlockT combinedBlock = Combine(block1, block2); //hypothetical function 

and will be equivalent to doing:

 MyBlockT combinedBlock = ^(NSString *string, id object) { block1(string, object); block2(string, object); }; 

I know this only makes sense with blocks that return void , but that is all that interests me.

Combine function requires only 2 blocks, if I have more, I can just link them. I am at the end about how to implement this or even possible.

PS I wouldn’t mind if C macros participated in the solution

EDIT

I would like to use the resulting block as an argument to a method, for example:

 [UIView animateWithDuration:1 animations:someCombinedBlock]; 
+2
source share
5 answers

Now on GitHub, WoolBlockInvocation !

These are a couple of the WSSBlockInvocation and WSSBlockSignature along with some supporting code that use libffi and the ObjC @encode @encode that the compiler generates for blocks so you can call up a complete list of blocks with the same set of arguments.

Any number of blocks can be added to the call object if their signatures - the value of the return type, the number and types of arguments - match. After setting the arguments of the call invocation object, the locks can be called in turn, and the return values, if any, are stored for later access.

The piece that you are particularly interested in stitching this list of blocks into one block is provided by the invocationBlock method of WSSBlockInvocation .

 - (id)invocationBlock { return [^void (void * arg1, ...){ [self setRetainsArguments:YES]; va_list args; va_start(args, arg1); void * arg = arg1; NSUInteger numArguments = [blockSignature numberOfArguments]; for( NSUInteger idx = 1; idx < numArguments; idx++ ){ [self setArgument:&arg atIndex:idx]; arg = va_arg(args, void *); } va_end(args); [self invoke]; } copy]; } 

This returns a block that (ab) uses varargs to defer the assignment of arguments until this encapsulating block actually calls itself. So you can do the following:

 WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:@[animationBlockOne, animationBlockTwo]]; void (^combinedAnimation)(void) = [invocation invocationBlock]; [UIView animateWithDuration:1 animations:combinedAnimation]; 

Of course, if you just worry about blocks for animations that take no arguments and have no return value, then creating a wrapper block is trivial:

 void (^combinedAnimation)(void) = ^{ animationBlock(); anotherAnimationBlock(); // etc. }; 

You only need my code if you need to wrap a set of blocks and call them all with the same set of arguments.

NB I tested this on OS X on x86_64, but not on any other platform . I hope that it works on ARM under iOS, but varargs is cool "not portable", and this may not be the case. Compile a compiler and let me know if something breaks.

+2
source

Is this what you are looking for?

 MyBlockT CombineBlocks(MyBlockT block1, MyBlockT block2) { return [^(NSString *string, id object) { block1(string, object); block2(string, object); } copy]; } 

The function creates a new block, which successively calls two given blocks.

+4
source

Here is a fun abuse of varargs:

 id combine(id block, ...) { NSMutableArray *blocks = [NSMutableArray array]; //[blocks addObject:block]; va_list objlist; va_start(objlist, block); //while((obj = va_arg(ap, id))) { // } for(id obj = block; obj; obj = va_arg(objlist, id)) { [blocks addObject:[obj copy]]; } va_end(objlist); void (^wrapper)(id,...) = ^(id arg, ...) { NSMutableArray *args = [NSMutableArray array]; va_list arglist; va_start(arglist, arg); for(id x = arg; x; x = va_arg(arglist, id)) { [args addObject:x]; } va_end(arglist); for(void (^blk)() in blocks) { blk(args); } }; return [wrapper copy]; } int main() { NSString *fmt = @"-%d-\n%@\n---"; void (^foo)() = combine(^(NSArray *a){ NSLog(fmt, 1, a); }, ^(NSArray *a){ NSLog(fmt, 2, a); }, nil); foo(@"first", @"second", nil); return 0; } 

You must define each block to accept NSArray arguments, and both combine and the subsequent block call must have at least one argument and end with nil .

If you know the method signature ahead of time, you can bypass NSArray and block the argument constraint by changing the block block accordingly.

+1
source

Since you are not against macros

 #define combinedBlock(string, object) \ block1((string), (object) ) \ block2((string), (object) ) 
0
source

if you need to complete 2 or more animations at the same time, RZViewActions is all you need. Its code looks almost like animateWithDuration:... , but with additional features.

If you need to execute ANY blocks at the same time, you need something like ReactiveCocoa . But I suggest you PromiseKit (simply because it's easier).

0
source

Source: https://habr.com/ru/post/1497638/


All Articles