Methods and optional parameters - objective-c

I have read in the Apple Documentation that we can use optional parameters in objective c methods call. Example from the Apple documentation :
Methods that take a variable number of parameters are also possible,
though they’re somewhat rare. Extra parameters are separated by commas
after the end of the method name. (Unlike colons, the commas are not
considered part of the name.) In the following example, the imaginary
makeGroup: method is passed one required parameter (group) and three
parameters that are optional:
[receiver makeGroup:group, memberOne, memberTwo, memberThree];
Can someone tell when to use this feature and how ? is there any example in the Apple API ?
thanks

The type of method you are describing is called a variadic method. Examples in Cocoa include +[NSArray arrayWithObjects:] and +[NSDictionary dictionaryWithObjectsAndKeys:]. You access the arguments of a variadic method (or function) using the macros defined in stdarg.h.
Here's an example of how the +[NSArray arrayWithObjects:] method might be implemented:
+ (NSArray *)arrayWithObjects:(id)firstObject, ... {
int count = 0;
va_list ap;
va_start(ap, firstObject);
id object = firstObject;
while (object) {
++count;
object = va_arg(ap, id);
}
va_end(ap);
id objects[count];
va_start(ap, firstObject);
object = firstObject;
for (int i = 0; i < count; ++i) {
objects[i] = object;
object = va_arg(ap, id);
}
va_end(ap);
return [self arrayWithObjects:objects count:count];
}

I've written a method like that once or twice. It's a bit of a pain. It works very much like parsing a command line in a C program.
I don't remember now where I found the documentation on how to do it. If I remember correctly, it uses functions va_start() and va_end().
A major down-side of that approach is that the comma-delimited list of parameters are not type checked, and don't have labels like they do in normal methods.
The main way it makes sense to use that approach is in a method like NSArray's arrayWithObjects, where you need to allow a variable-sized list of parameters of any type, or NSLog.

Related

How do Objective-C comparison blocks know their arguments for the sortUsingComparator method?

I'm in the process of learning Objective-C, and am trying to get my head wrapped around the idea of using blocks.
In an example I'm seeing, there is a method meant to sort an NSArray named book in alphabetical order by name:
-(void) sort
{
[book sortUsingComparator:
^(id obj1, id obj2) {
return [[obj1 name] compare: [obj2 name]];
} ];
}
How does the method pass the objects to the obj1 and obj2 parameters? Does the sortUsingComparator somehow automatically pass 2 objects to its comparator?
sortUsingComparator: iterates over the array and calls the block with various pairs of objects to determine how to sort them. There's no magic here — it's a normal sorting function, but it uses your block instead of > to compare the values.
Well, depending on the implementation of the sort, every time the sort algorithm needs to compare 2 elements this will get called. You don't really need to know what will get passed to it, just define an order relationship for the types that get passed. It'll ask what he needs to know.
Some extra information about blocks that answers your question, a block can be assigned to a variable and called like a function. The sortUsingComparator: method signature is:
- (void)sortUsingComparator:(NSComparator)cmptr
cmptr is the variable that holds your block. The sortUsingComparator: method will run every element in the array through its sort algorithm and, as Fernando pointed out, every time the sort algorithm needs to compare 2 elements it's called like this:
NSComparisonResult result = cmptr(obj1, obj2);
And your block code is executed. So the answer to your question:
Does the sortUsingComparator somehow automatically pass 2 objects to its comparator?
is yes :)
The sort method does something like this (I'm not claiming the sort: method does this exactly, but the idea remains):
for (i = 0; i < [book count]; i++) {
id currentObject = [book objectAtIndex:i]; // obj1
for (j = 0; j < i; j++) {
id sortedObject = [book objectAtIndex:j]; // obj2
NSComparisonResult result = sortBlock(currentObject, sortedObject);
// Insert currentObject where appropriate; perform the rest of the sort
}
}
In this case, I'm assuming that the block's parameter name is ^sortBlock (the caret denotes a block). You invoke blocks just like regular functions, with parentheses and arguments.
So to answer your question…it's automatic in the sense that you don't have to think about it, but there's no magic going on.
Hope this helps!

Variable number of method parameters in Objective C - Need an example

From Objective C Programming Guide (Under the "Object Messaging" section),
Methods that take a variable number of parameters are also possible,
though they’re somewhat rare. Extra parameters are separated by commas
after the end of the method name. (Unlike colons, the commas are not
considered part of the name.) In the following example, the imaginary
makeGroup: method is passed one required parameter (group) and three
parameters that are optional:
[receiver makeGroup:group, memberOne, memberTwo, memberThree];
I tried to create such a method and it shows an error
"Expected ';' after method prototype"
when I try to declare the below function in my interface file(.h file).
- (void) printMyClass: (int) x, (int) y, (int) z;
Can anyone give sample example to create such a method like makeGroup
Thank you
You can see this link.
In your header file define the methods with three dots at the end
-(void)yourMethods:(id)string1,...;
And in you implementation file write the methods body
-(void)yourMethods:(id)string1, ...{
NSMutableArray *arguments=[[NSMutableArray alloc]initWithArray:nil];
id eachObject;
va_list argumentList;
if (string1)
{
[arguments addObject: string1];
va_start(argumentList, string1);
while ((eachObject = va_arg(argumentList, id)))
{
[arguments addObject: eachObject];
}
va_end(argumentList);
}
NSLog(#"%#",arguments);
}
Now call your method
[self yourMethods:#"ab",#"cd",#"ef",#"gf",nil];
NOTE: remember to put nil at the end
The syntax for declaring a method with a variable number of arguments is like this:
- (void) printMyClass: (int) x, ...;
One argument is always the required minimum, the others can be accessed via the va_arg function group. For the exact details, see this tutorial.

Function pointer problem in Objective-C

So I am trying to store a series of methods in an array (if that made sense).
void *pointer[3];
pointer[0] = &[self rotate];
pointer[1] = &[self move];
pointer[2] = &[self attack];
//...
What I am trying to do is have an array of stuff and based on the type of the object in the array, a certain method is invoked. And instead of having a bunch of if statement saying something like:
if ([[myArray objectAtIndex:0] type] == robot]) {
//Do what robots do...
}
else if (...) {
}
else {
}
And having this in a timer I was hoping to make it something like this:
pointer[[[myArray objectAtIndex:0] type]]; //This should invoke the appropriate method stored in the pointer.
Right now the code above says (the very first block of code):
Lvalue required as unary '&' operand.
If you need any clarification just ask.
Also, just to let you know all the method I am calling are type void and don't have any parameters.
You can't just make a function pointer out of an Objective-C function using the & Operator.
You'll want to look into:
#selector
NSInvocation
Blocks
Any of these can do what you want. Definitely read about selectors (the #selector compiler directive and the SEL type) if you're unfamiliar with that (it's a basic concept that you'll need a lot). Blocks are fairly new (available since Mac OS X 10.6 and iOS 4) and they'll save you a ton of work where you would have needed target/selector, NSInvocation or callback functions on earlier versions of Mac OS X and iOS.
Use function pointers if you need to pass around references to C functions but when working with methods on Objective-C objects you should really use selectors and the SEL type.
Your code would then be something like:
SEL selectors[3];
selectors[0] = #selector(rotate);
selectors[1] = #selector(move);
selectors[2] = #selector(attack);
...
[self performSelector:selectors[n]];

How to determine whether a #selector wants a parameter?

The scenario presents itself where I have an object that stores an outside #selector for later use. By design, I would like to be able to add two kinds of selectors. The simple one, without parameters, like [object add:#selector(doSomething)], and the more complex one, with one parameter, like [object add:#selector(doSomething:)] (mind the colon). Let's say the selector is stored in a variable SEL mySelector.
In the execution, I need to decide between [anotherObject performSelector:mySelector] or [anotherObject performSelector:mySelector withObject:userInfo]].
The way I implemented this decision, is by providing a BOOL flag that redundantly stores whether the performance should be with or without the extra parameter. Yet although I can't find this in the docs, I have the feeling that I should also be able to ask the selector something like -(BOOL)needsParameter. I know, for example, that UIGestureRecognizer's addTarget:action: somehow makes this distinction automatically.
Could someone point me in the right direction?
You can use the NSMethodSignature class for that. For instance,
SEL mySelector = …;
NSMethodSignature *msig = [anotherObject methodSignatureForSelector:mySelector];
if (msig != nil) {
NSUInteger nargs = [msig numberOfArguments];
if (nargs == 2) { // 0 non-hidden arguments
}
else if (nargs == 3) { // 1 non-hidden argument
}
else {
}
}
Alternatively, you could use NSStringFromSelector() to get the string representation of mySelector and count the number of occurrences of the colon character.

iterate through object properties and change current property in a loop in objective-c

Newbie question: Can one substitue commands, properties or methods for NSStrings or char at runtime?
Lets say I do something like this:
//these are the names of my properties of an NSObject I created
char theChangingProperty[17][18] = {"name", "title", "number", "property4", "property5", "property6", "property7", "property8", "property9", "property10", "property11", "property12", "property13", "property14", "property15", "property16", "property17"};
(PS I know there is a better actual objective-c way of getting an objects properties, I even found a post with this Get an object properties list in Objective-C)
anyway, how does one iterate through a list and have the property name change? Lets say I'm using dot notation at runtime during a loop... If I wanted to set 17 (or 100!) different properties of my object all to values of some other array all at once, like this:
for (int t=1; t<17; t++) {
myObject.theChangingProperty[t] = valueOfAnotherArray[t];
}
I know objective-c is going to look for an actual property called "theChangingProperty". But I want that to be an array that will spit out the actual property that would change with each loop iteration (name, title, number, property4, etc)
Thanks for your time in answering a newbie question :)
What you want is called key-value coding. Specifically, the line you're trying to write is [myObject setValue:valueOfAnotherArray[t] forKey:[NSString stringWithUTF8String:theChangingProperty[t]]].
Dot notation is resolved at compile time, not runtime, therefore you cannot use it for this purpose. You'll have to use the setter name for this. i.e., setTheChangingProperty: and performSelector: with the appropriate object to pass in as the value.
Note that in a simple case, this will probably work just fine, however, someone could have gone in and done something like:
#property (nonatomic, retain, setter=fooBarBaz:) id blurgle;
You in the above case, would not be simply able to assume setBlurgle: is the right setter.
You can use setValue:forKey: but note that property keys need to be NSStrings, not C strings so declare your array:
NSString* theChangingProperty[17] = { #"name", ....
Then you would write:
for (int t = 0; t < 17; t++)
{
[myObject setValue: valueOfAnotherArray[t] forKey: theChangingProperty[t]];
}
Or you can use performSelector:withObject: but then your array needs to contain selectors
SEL theChangingProperty[17] = { #selector(setName:), ....
for (int t = 0; t < 17; t++)
{
[myObject performSelector: theChangingProperty[t] withObject: valueOfAnotherArray[t]];
}