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

I often find myself creating a "wrapper" block which just serves to execute a number of other blocks, usually with the same type signature.
Say I have 2 blocks with the same type signature:
MyBlockT block1 = ^(NSString *string, id object) {
//1 does some work
};
MyBlockT block2 = ^(NSString *string, id object) {
//2 does some other work
};
Is there some way to implement the magic function Combine() which would take 2 blocks:
MyBlockT combinedBlock = Combine(block1, block2); //hypothetical function
and 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's all I'm interested in.
The Combine function needs only take in 2 blocks, if I have more I can just chain them. I'm at wits end on how to go about implementing this or whether it's even possible.
P.S. I wouldn't mind if the solution involved C macros
EDIT
I'd like to be able to use the resulting block as a method argument, e.g.:
[UIView animateWithDuration:1 animations:someCombinedBlock];

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 that calls the two given blocks sequentially.

Now up on GitHub, WoolBlockInvocation!
This is a pair of classes, WSSBlockInvocation and WSSBlockSignature, along with some supporting code, that leverage libffi and the ObjC #encode strings which the compiler generates for Blocks to allow you to invoke a whole list of Blocks with the same set of arguments.
Any number of Blocks can be added to an invocation object, provided their signatures -- meaning return type and number and types of arguments -- match. After setting arguments on the invocation object, the Blocks can be invoked in turn, with the return values, if any, stored for later access.
The piece that you're particularly interested in, sewing that list of Blocks up into a single 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 functionality to defer assigning arguments until that encapsulating Block is actually invoked itself. You can thus do the following:
WSSBlockInvocation * invocation = [WSSBlockInvocation invocationWithBlocks:#[animationBlockOne, animationBlockTwo]];
void (^combinedAnimation)(void) = [invocation invocationBlock];
[UIView animateWithDuration:1 animations:combinedAnimation];
Of course, if you're just worried about Blocks for animations, that take no arguments and have no return value, constructing 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 invoke them all with the same set of arguments.
N.B. I have tested this on OS X on x86_64, but not on any other platform. I hope it works on ARM under iOS, but varargs is famously "not portable" and it may not. Caveat compilor, and let me know if something breaks.

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 an NSArray of arguments, and both the combine and resulting block invocation must have at least one argument and end in nil.
If you know the method signature ahead of time, you can work around the NSArray and block arguments restriction by altering the wrapper block appropriately.

Since you don't mind macros
#define combinedBlock(string, object) \
block1((string), (object) ) \
block2((string), (object) )

if you need to perform 2 or more animations simultaneously then RZViewActions is everything you need. Its code looks like almost as animateWithDuration:... calls but with additional features.
If you need to perform ANY blocks simultaneously then you need something like ReactiveCocoa. But I suggest you PromiseKit (simply because it is easier).

Related

Objective-C: Passing an IF statement as a argument

I need to pass an IF statement to a method. In JavaScript you can assign a function to a variable. Then that variable can be passed to a function and executed. Does this exist in Objective-C?
This is the pattern I'd like to implement:
-(void)singleComparisonWith:(NSArray *)data
IndexBegin:(NSUInteger)indexBegin
IndexEnd:(NSUInteger)indexEnd
Threshold:(float)threshold {
NSIndexSet *set1 = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationConcurrent
Comparison:XXXXXXXXX];
// XXXXXXXXX is an IF statement that looks for value at an index above threshold
}
-(void)rangeComparisonWith:(NSArray *)data
IndexBegin:(NSUInteger)indexBegin
IndexEnd:(NSUInteger)indexEnd
ThresholdLow:(float)thresholdLow
ThresholdHigh:(float)thresholdHigh {
NSIndexSet *candidates = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationReverse
Comparison:YYYYYYYYY];
// YYYYYYYYY is an IF statement that looks for value at an index above thresholdLow and above thresholdHigh
}
-(NSIndexSet *)searchWithData:data
Range:(NSIndexSet *)range
Option:(NSEnumerationOptions)option
Comparison:(id)comparison {
return [data indexesOfObjectsAtIndexes:range
options:option
passingTest:^(id obj, NSUInteger idx, BOOL *stop){
// Comparison is used here. Returns YES if conditions(s) are met.
}
];
}
EDIT:
Here's the solution thanks to #Charles Srstka.
NSIndexSet *set1 = [self searchWithData:data
Range:[self makeInspectionWithRange:indexBegin
End:indexEnd]
Option:NSEnumerationConcurrent
Comparison:BOOL^(id o) {
return ([o floatValue] > threshold);
}
];
-(NSIndexSet *)searchWithData:data
Range:(NSIndexSet *)range
Option:(NSEnumerationOptions)option
Comparison:(BOOL(^)(id o))comparison {
return [data indexesOfObjectsAtIndexes:range
options:option
passingTest:^(id obj, NSUInteger idx, BOOL *stop){
return comparison(obj);
}
];
No errors in that segment.
Thank you for your help.
What you want in Objective-C is called block syntax. While certainly not the nicest thing to look at, or the easiest thing to remember, it will do what you want.
// declares a block named 'foo' (yes, the variable name goes inside the parens)
NSUInteger (^foo)(NSString *) = ^(NSString *baz) {
return [baz length];
};
// now you can call foo like a function:
NSUInteger result = foo(#"hello world");
// or pass it to something else:
[someObject doSomethingWith:foo];
// A method that takes a block looks like this:
- (void)doSomethingWith:(NSUInteger (^)(NSString *))block;
This site is a handy "cheat sheet" that lists all the ways to declare a block in Objective-C. You will probably be referring to it often. The URL I linked to is a newer, work-friendly mirror. I'm sure you can guess the site's original URL if you think about it. ;-)
Basically whenever you see a ^ in Objective-C, you're looking at a block declaration. Unless, of course, you're looking at an XOR operation. But usually it's a block.
EDIT: Look at the site I linked to, where it says "as an argument to a method call." You need to declare it using that syntax, i.e.
... comparison: ^BOOL(id o) {
return ([o floatValue] > threshold);
}];
I know it's not the most intuitive syntax in the world, which is why that site is useful as a cheat sheet.
Also, unrelated to your issue, but Objective-C naming convention is to start the argument labels with lower-case letters; i.e. range:, options:, and comparison: rather than Range:, Option:, Comparison:.

How can I create a block that 'wraps' a target/selector pair?

I love blocks, and they are very cool.
However, I find that blocks can clutter up my code and make it harder to read without folding all of them up inside Xcode (which I don't like doing).
I like splitting my code into logical methods (selectors) to keep it easier to read, but it appears (on the surface) that this isn't readily possible with frameworks like dispatch, AFNetworking, and several others.
I also don't care for the delegate approach, as that means I cannot name my methods as I would like to, instead relying on what other people think I need.
So, without writing a bunch of glue code like this:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:^(NSArray *events) { [self processEvents:events] }];
...
}
I could instead do something like this:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:createBlock(self, #selector(processEvents:))];
...
}
Which is easier to read (to me).
With the power that we have with objective-c, and it's runtime, this should be possible, no? I haven't seen anything like this out there, though.
I liked your answer from an academic standpoint; +1 and, clearly, you learned something.
From a practical perspective, it seems like an awful lot of added fragility for very little reduction in typing while it also leads to some information loss at the call site.
The advantage of this is that it is exactly explicit:
-(void) reloadData {
...
[[EventsManager instance] data:YES async:^(NSArray *events) { [self processEvents:events] }];
...
}
Reading that, one sees that the asynchronous callback block is required to process the arguments and that the processEvents: method on self will be used to do the actual work.
The expression createBlock(self, #selector(processEvents:)) is a lossy representation of the same; it loses the explicit argumentation of the callback and the mapping between that argumentation and the method being invoked (I often see callback blocks like the above with multiple arguments where there is some lightweight logic and/or argument processing before invoking the method).
Note also that processing a non-varargs call site as a varargs when called is a violation of the C standard and will not work on certain ABIs with certain lists of arguments.
Yes, this is indeed possible, but this solution is ABI-specific (not guaranteed to work on all platforms), and makes extensive use of the information available at run-time about methods.
What we first must do is get information about the method we are wrapping with the block. This is done via NSMethodSignature, which contains information such as:
Number of arguments
Size (in bytes) of each argument
Size of return type
This allows us to wrap (almost) any method with no specific code for that method, thus creating a re-usable function.
Secondly, we need a way to safely dispatch method calls at run-time. We do this via NSInvocation, which grants us the ability to create a dynamic, and safe, method call at run-time.
Thirdly, we need to have a block that can take any number of arguments passed in, and the dispatch that. This is done via C's va_list APIs, and should work for 99% of methods.
Finally, we need to get the return value, and be able to return that from our block. This is the part of the entire operation that is possible to not work, because of weirdness with returning structs and such with the Objective-C runtime.
However, as long as you keep to primitive types and Objective-C objects, this code should work great for you.
A couple of things to note about this implementation:
It is reliant upon undefined behavior with casting of block & function types, however, because of the calling conventions of iOS and Mac, this should not pose any issues (unless your method has a different return type than what the block expects).
It also relies upon undefined behavior with the result of calling va_arg with a type that may not be what is passed - however, since the types are of the same size, this should never be an issue.
Without any further ado, here is an example of the code, followed by the implementation:
#interface MyObj : NSObject
-(void) doSomething;
#end
#implementation MyObj
-(void) doSomething
{
NSLog(#"This is me, doing something! %p", self);
}
-(id) doSomethingWithArgs:(long) arg :(short) arg2{
return [NSString stringWithFormat:#"%ld %d", arg, arg2];
}
#end
int main() {
// try out our selector wrapping
MyObj *obj = [MyObj new];
id (^asBlock)(long, short) = createBlock(obj, #selector(doSomethingWithArgs::));
NSLog(#"%#", asBlock(123456789, 456));
}
/* WARNING, ABI SPECIFIC, BLAH BLAH BLAH NOT PORTABLE! */
static inline void getArgFromListOfSize(va_list *args, void *first, size_t size, size_t align, void *dst, BOOL isFirst) {
// create a map of sizes to types
switch (size) {
// varargs are weird, and are aligned to 32 bit boundaries. We still only copy the size needed, though.
// these cases should cover all 32 bit pointers (iOS), boolean values, and floats too.
case sizeof(uint8_t): {
uint8_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
case sizeof(uint16_t): {
uint16_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
case sizeof(uint32_t): {
uint32_t tmp = isFirst ? (uint32_t) first : va_arg(*args, uint32_t);
memcpy(dst, &tmp, size);
break;
}
// this should cover 64 bit pointers (Mac), and longs, and doubles
case sizeof(uint64_t): {
uint64_t tmp = isFirst ? (uint64_t) first : va_arg(*args, uint64_t);
memcpy(dst, &tmp, size);
break;
}
/* This has to be commented out to work on iOS (as CGSizes are 64 bits)
// common 'other' types (covers CGSize, CGPoint)
case sizeof(CGPoint): {
CGPoint tmp = isFirst ? *(CGPoint *) &first : va_arg(*args, CGPoint);
memcpy(dst, &tmp, size);
break;
}
*/
// CGRects are fairly common on iOS, so we'll include those as well
case sizeof(CGRect): {
CGRect tmp = isFirst ? *(CGRect *) &first : va_arg(*args, CGRect);
memcpy(dst, &tmp, size);
break;
}
default: {
fprintf(stderr, "WARNING! Could not bind parameter of size %zu, unkown type! Going to have problems down the road!", size);
break;
}
}
}
id createBlock(id self, SEL _cmd) {
NSMethodSignature *methodSig = [self methodSignatureForSelector:_cmd];
if (methodSig == nil)
return nil;
return ^(void *arg, ...) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setTarget:self];
[invocation setSelector:_cmd];
NSUInteger argc = [methodSig numberOfArguments];
va_list args;
va_start(args, arg);
for (int argi = 2; argi < argc; argi++) {
const char *type = [methodSig getArgumentTypeAtIndex:argi];
NSUInteger size;
NSUInteger align;
// get the size
NSGetSizeAndAlignment(type, &size, &align);
// find the right type
void *argument = alloca(size);
getArgFromListOfSize(&args, arg, size, align, argument, argi == 2);
[invocation setArgument:argument atIndex:argi];
}
va_end(args);
[invocation invoke];
// get the return value
if (methodSig.methodReturnLength != 0) {
void *retVal = alloca(methodSig.methodReturnLength);
[invocation getReturnValue:retVal];
return *((void **) retVal);
}
return nil;
};
}
Let me know if you have any issues with this implementation!

Checking Objective-C block type?

This is primarily a curiosity, I'm not really sure what's the practical use of this but here goes.
Since blocks are also Objective-C objects, is it possible to check their type? That is, does it respond to the isKindOfClass: message and how to use that message with respect to blocks?
My naive thought that it's probably like this:
-(void) aMethod {
typedef int (^BlockA)(int x, int y);
id blockVar = ...; // get a block from somewhere
if([blockVar isKindOfClass:BlockA]) {
BlockA blockVarA = blockVar;
int result = blockVarA(1,2);
}
}
The code above probably won't work. But if it is possible to check a block's type, what is the correct way to do it?
Can do, kinda sorta.
But first, let's disambiguate. -[NSObject isKindOfClass:] can tell you it's a block, and that's about it. E.g. I believe this line of code -- ostensibly & unfortunately A BAD IDEA -- will return YES for blocks on present Lion & iOS 5.x:
[myBlock isKindOfClass:NSClassFromString(#"NSBlock")]
That won't help you distinguish the block's function signature.
But it can be done, by snagging the signature from the block's documented internal struct. Code follows for an example OS X command-line app, much of which ripped from Mike Ash's MABlockClosure (great detailed explanation). (UPDATE: Github project CTObjectiveCRuntimeAdditions also apparently provides library code for just this purpose.)
#import <Foundation/Foundation.h>
struct BlockDescriptor {
unsigned long reserved;
unsigned long size;
void *rest[1];
};
struct Block {
void *isa;
int flags;
int reserved;
void *invoke;
struct BlockDescriptor *descriptor;
};
static const char *BlockSig(id blockObj)
{
struct Block *block = (void *)blockObj;
struct BlockDescriptor *descriptor = block->descriptor;
int copyDisposeFlag = 1 << 25;
int signatureFlag = 1 << 30;
assert(block->flags & signatureFlag);
int index = 0;
if(block->flags & copyDisposeFlag)
index += 2;
return descriptor->rest[index];
}
int main(int argc, const char * argv[])
{
#autoreleasepool {
int (^block)(NSNumber *) = ^(NSNumber *num) {
NSLog(#"%# %#", NSStringFromClass([num class]), num);
return [num intValue];
};
NSLog(#"signature %s", BlockSig(block));
NSLog(#"retval %d", (int)block([NSNumber numberWithInt:42]));
}
return 0;
}
Run this and you should get something like:
[58003:403] signature i16#?0#8
[58003:403] __NSCFNumber 42
[58003:403] retval 42
The numbers in the signature (I'm told they are offsets) can be stripped for simpler i#?#.
The signature is in the #encode format, which isn't perfect (e.g. most objects map to same #), but should afford you some ability to distinguish blocks with different signatures at runtime.
While it's not documented in the Apple link, my testing points to #? being the code for a block type, which makes sense of the signature above. I found a clang-developers discussion on this issue which seems to back this up.
The "BlockA" in (^BlockA) is the variable name (in this case a typedef), not its class.
Blocks are objects, but not regular subclasses of NSObject. They only implement a subset of the methods. -isKindOfClass: will probably just crash.
Blocks are of the type NSMallocBlock or NSConcreteGlobalBlock, ... depending on where they were created (heap, stack, ...).
It seems that blocks are of classes like __NSGlobalBlock__, __NSStackBlock__, or __NSMallocBlock__, etc., whose inheritance chain eventually goes to NSBlock and then NSObject. So you could test whether something is a block by doing [... isKindOfClass:NSClassFromString(#"NSBlock")]. However, there doesn't seem to be any way to query a block's signature (return type and argument types) at runtime, so you wouldn't be able to distinguish between blocks of different signatures.
As well as Apple having nothing I can find to say on the matter, poking at a block with class_copyMethodList and method_getName reveals no obvious exposed methods. So I'm going to say that it isn't possible to check their type.
A old question, but anyway:
If you want a simple way of doing this: (Compile it with -fno-objc-arc)
Class __NSGlobalBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_block_t thisIsAGlobalBlock = ^{// a block with no variables will be a __NSGlobalBlock__
};
result = [[thisIsAGlobalBlock class] retain];
});
return result;
};
Class __NSStackBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAStackBlock = ^{
return ;// we really DON'T want infinate recursion
thisIsAStackBlock();// including a reference to __block var makes this a __NSStackBlock__
};
result = [[thisIsAStackBlock class] retain];
});
return result;
};
Class __NSMallocBlock__CLASS () {
static Class result = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block dispatch_block_t thisIsAMallocBlock = Block_copy(// << turns the __NSStackBlock__ Block into a __NSMallocBlock__
^{
return ;// we really DON'T want infinate recursion
thisIsAMallocBlock();// including a reference to __block var makes this a __NSStackBlock__
});
result = [[thisIsAMallocBlock class] retain];
Block_release(thisIsAMallocBlock);
});
return result;
};
Test Code:
#autoreleasepool {
__block dispatch_block_t iAmAGlobalBlock = ^{
};
__block dispatch_block_t iAmAStackBlock = ^{
return;
iAmAStackBlock();
};
dispatch_block_t iAmHeapBlock = Block_copy(iAmAStackBlock);
dispatch_block_t iAmNotAHeapBlock = Block_copy(iAmAGlobalBlock);
if ([iAmAGlobalBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(#"very great success!");
}
if ([iAmAStackBlock isKindOfClass:__NSStackBlock__CLASS()]) {
NSLog(#"another great success!");
}
if ([iAmHeapBlock isKindOfClass:__NSMallocBlock__CLASS()]) {
NSLog(#"also great success!");
}
if ([iAmNotAHeapBlock isKindOfClass:__NSGlobalBlock__CLASS()]) {
NSLog(#"yet another great success!");
}
NSLog (#"Block classes, as reported by NSStringFromClass():\n__NSGlobalBlock__CLASS() = %#\n__NSStackBlock__CLASS() = %#\n__NSMallocBlock__CLASS() = %#\n[iAmAGlobalBlock class] = %#\n[iAmAStackBlock class] = %#\n[iAmHeapBlock class] = %#\n[iAmNotAHeapBlock class] = %#\n",
NSStringFromClass(__NSGlobalBlock__CLASS()),
NSStringFromClass(__NSStackBlock__CLASS()),
NSStringFromClass(__NSMallocBlock__CLASS()),
NSStringFromClass([iAmAGlobalBlock class]),
NSStringFromClass([iAmAStackBlock class]),
NSStringFromClass([iAmHeapBlock class]),
NSStringFromClass([iAmNotAHeapBlock class])
);
Block_release(iAmHeapBlock);
Block_release(iAmNotAHeapBlock);// not really needed, but since we did "Block_copy" it...
}

Function that returns a function

How to assign and subsequently call a function that returns a function to a local variable in Objective-C?
UPDATE:
I've come up with the following but it's still not right I'm afraid:
(void (^)()) (^loadedCallback) () = (void (^)()) ^(){
#synchronized (synchronizer) {
semaphore++;
}
return Block_copy(^{
#synchronized (synchronizer) {
semaphore--;
if (semaphore == 0) {
onAllLoaded();
}
}
}); };
First, you need to understand the function pointer declaration syntax. It's the same for blocks, except that it's a ^ instead of a *.
Then, you need to create a block and return a copy of it, and assign that to a correctly-declared variable.
typedef NSArray* (^my_block_type_t)(int, float);
my_block_type_t createBlock()
{
my_block_type_t block = ^(int a, float b)
{
return [NSArray array];
};
return Block_copy(block);
}
/* snip */
my_block_type_t theBlock = createBlock();
theBlock();
Block_release(theBlock);
EDIT to address OP's edit: typedefs are typically used to make code easier to read. In the case of blocks and function pointers, it also makes it easier to write. There is a built-in typedef (dispatch_block_t) for blocks that accept no arguments and return void; you should use it. You should also make as many typedefs as you need to avoid having to use the ugly declaration syntax function pointers otherwise force onto your code.
typedef dispatch_block_t (^block_creator_t)();
block_creator_t loadedCallback = ^{
#synchronized (synchronizer)
{
semaphore++;
}
dispatch_block_t result = ^{
#synchronized (synchronizer)
{
semaphore--;
if (semaphore == 0)
onAllLoaded();
}
};
return Block_copy(result);
};

Objective c implement method which takes array of arguments

Hee
Does anybody know how to implement an method in objective c that will take an array of arguments as parameter such as:
[NSArray arrayWithObjects:#"A",#"B",nil];
The method declaration for this method is:
+ (id)arrayWithObjects:(id)firstObj...
I can't seem to make such method on my own. I did the following:
+ (void) doSometing:(id)string manyTimes:(NSInteger)numberOfTimes;
[SomeClass doSometing:#"A",#"B",nil manyTimes:2];
It will give the warningtoo many arguments to function 'doSometing:manyTimes:'
Thanks already.
The ellipsis (...) is inherited from C; you can use it only as the final argument in a call (and you've missed out the relevant comma in your example). So in your case you'd probably want:
+ (void)doSomethingToObjects:(id)firstObject, ...;
or, if you want the count to be explicit and can think of a way of phrasing it well:
+ (void)doManyTimes:(NSInteger)numberOfTimes somethingToObjects:(id)firstObject, ...;
You can then use the normal C methods for dealing with ellipses, which reside in stdarg.h. There's a quick documentation of those here, example usage would be:
+ (void)doSomethingToObjects:(id)firstObject, ...
{
id object;
va_list argumentList;
va_start(argumentList, firstObject);
object = firstObject;
while(1)
{
if(!object) break; // we're using 'nil' as a list terminator
[self doSomethingToObject:object];
object = va_arg(argumentList, id);
}
va_end(argumentList);
}
EDIT: additions, in response to comments. You can't pass the various things handed to you in an ellipsis to another function that takes an ellipsis due to the way that C handles function calling (which is inherited by Objective-C, albeit not obviously so). Instead you tend to pass the va_list. E.g.
+ (NSString *)doThis:(SEL)selector makeStringOfThat:(NSString *)format, ...
{
// do this
[self performSelector:selector];
// make string of that...
// get the argument list
va_list argumentList;
va_start(argumentList, format);
// pass it verbatim to a suitable method provided by NSString
NSString *string = [[NSString alloc] initWithFormat:format arguments:argumentList];
// clean up
va_end(argumentList);
// and return, as per the synthetic example
return [string autorelease];
}
Multiple arguments (also known as an arglist) can only come at the end of a method declaration. Your doSomething method would look something like this:
+ (void)doNumberOfTimes:(NSInteger)numberOfTimes withStrings:(id)firstArg, ...
{
va_list args;
va_start(args, firstArg);
NSString * argString = firstArg;
while (argString != nil)
{
// do something with argString here
argString = va_arg(args, NSString *);
}
va_end(args);
}
To be called as follows:
[SomeClass doNumberOfTimes:2 withStrings:#"A", #"B", nil];
See also: How to create variable argument methods in Objective-C
I think you're after a variadic function. Here's Apple's documentation: http://developer.apple.com/library/mac/qa/qa2005/qa1405.html