How to determine whether a #selector wants a parameter? - objective-c

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.

Related

Obj C - Is there a way to make a method return the method in which it is called?

I want to know whether there is the way to achieve the following requirement:
First, there is a methodA, which takes an input objA and check whether objA is valid for the method. If it's valid, then it returns an object objB. However, if objA is not valid, it just returns objA itself. Note that even if objA is valid, the returned objB may still be the same with objB.
Then, there is a methodB and in the method, methodA is called. If the input of methodA is valid, the program go on without error. However, if methodA is called with invalid input, then methodB should terminate (a.k.a return with a certain string signaling an error) to prevent future crash.
The current solution that I could think of is to create a (BOOL)methodAInputIsValid:(obj)input and in methodB there is:
if(methodAInputIsValid:input) {
obj objReturn = methodA:input;
}else{
//show error warning
return;
}
//continue doing something with objReturn and finally return some valid thing
A problem of the code is that (BOOL)methodAInputIsValid:(obj)input and methodA:(obj)input share a lot of code in common because there is a validity-test in methodA. So I want to use the validity-test more efficiently so that it will work for methodB and drop the (BOOL)methodAInputIsValid.
Here is one possible application that I could think of:
In a program the user is asked to enter a string that will direct the action of the program. However, the input string may need to be standardized such as converting all letters to lower-case and converting all single quotation to double quotation and auto-complete any missing right parenthesis or quotation marks. However, if the user is just entering nonsense that cannot be standardized, then the program should terminate and warn the user of the problem. The two methods are (BOOL)isStandardizeable:(NSString *)input and (NSString *)standardize:(NSString *)input. Because (NSString *)standardize already returns an NSString *, I can't make it to return another BOOL, right? So the following code is not achievable:
if(standardize:input){
NSString *result = standardize:input;
} else {
NSLog(#"unrecognizable input!");
return;
}
//continue...
Can anyone think of a way to do this?
Realistically, I'd just have methodA return nil if the input is not valid. This is a fairly common method to signal failure to a caller. If you really need the "identity" part of the method, just have callers do
id res = [... methodA:input];
if(res == nil)
res = input;

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!

Methods and optional parameters

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.

Passing arguments by value or by reference in objective C

I'm kind of new with objective c and I'm trying to pass an argument by reference but is behaving like it were a value. Do you know why this doesn't work?
This is the function:
- (void) checkRedColorText:(UILabel *)labelToChange {
NSComparisonResult startLaterThanEnd = [startDate compare:endDate];
if (startLaterThanEnd == NSOrderedDescending){
labelToChange.textColor = [UIColor redColor];
}
else{
labelToChange.textColor = [UIColor blackColor];
}
}
And this is the call:
UILabel *startHourLabel; // This is properly initialized in other part of the code
[self checkRedColorText:startHourLabel];
Thanks for your help
Objective-C only support passing parameters by value. The problem here has probably been fixed already (Since this question is more than a year old) but I need to clarify some things regarding arguments and Objective-C.
Objective-C is a strict superset of C which means that everything C does, Obj-C does it too.
By having a quick look at Wikipedia, you can see that Function parameters are always passed by value
Objective-C is no different. What's happening here is that whenever we are passing an object to a function (In this case a UILabel *), we pass the value contained at the pointer's address.
Whatever you do, it will always be the value of what you are passing. If you want to pass the value of the reference you would have to pass it a **object (Like often seen when passing NSError).
This is the same thing with scalars, they are passed by value, hence you can modify the value of the variable you received in your method and that won't change the value of the original variable that you passed to the function.
Here's an example to ease the understanding:
- (void)parentFunction {
int i = 0;
[self modifyValueOfPassedArgument:i];
//i == 0 still!
}
- (void)modifyValueOfPassedArgument:(NSInteger)j {
//j == 0! but j is a copied variable. It is _NOT_ i
j = 23;
//j now == 23, but this hasn't changed the value of i.
}
If you wanted to be able to modify i, you would have to pass the value of the reference by doing the following:
- (void)parentFunction {
int i = 0; //Stack allocated. Kept it that way for sake of simplicity
[self modifyValueOfPassedReference:&i];
//i == 23!
}
- (void)modifyValueOfPassedReference:(NSInteger *)j {
//j == 0, and this points to i! We can modify i from here.
*j = 23;
//j now == 23, and i also == 23!
}
Objective-C, like Java, only has pass-by-value. Like Java, objects are always accessed through pointers. "objects" are never values directly, hence you never assign or pass an object. You are passing an object pointer by value. But that does not seem to be the issue -- you are trying to modify the object pointed to by the pointer, which is perfectly allowed and has nothing to do with pass-by-value vs. pass-by-reference. I don't see any problem with your code.
In objective-c, there is no way to pass objects by value (unless you explicitly copy it, but that's another story). Poke around your code -- are you sure checkRedColorText: is called? What about [startDate compare:endDate], does it ever not equal NSOrderedDescending? Is labelToChange nil?
Did you edit out code between this line
UILabel *startHourLabel;
and this line?
[self checkRedColorText:startHourLabel];
If not, the problem is that you're re-declaring your startHourLabel variable, so you're losing any sort of initialization that was there previously. You should be getting a compiler error here.
Here are the possibilities for why this doesn't work:
the label you pass in to checkRedColorText is not the one you think it is.
the comparison result is always coming out the same way.
... actually, there is no 3.
You claim you initialised startHourLabel elsewhere, but, if it is a label from a nib file, you should not be initialising it at all. It should be declared as an IBOutlet and connected to the label in the nib with interface builder.
If it is not a label in the nib i.e. you are deliberately creating it programmatically, you need to check the address of the label you initialise and check the address of the label passed in to checkRedColorText. Either NSLog its address at initialisation and in checkRedColorText or inspect it with the debugger.

What does it mean to return an object in a method?

I still cannot understand what does it mean to return an object in a method. What would its value mean?
If I have something like this:
-(ClassName *) methodName: (int) arg {
return arg;
}
I can't understand how an object can be returned through a method as the above. If someone can help me understand.
Thanks.
You would return an object by returning an object. For example, you could ignore the argument:
- (ClassName *)methodName:(int)arg {
return [[[ClassName alloc] init] autorelease];
}
You could turn the int into an object:
- (NSNumber *)methodName:(int)arg {
return [NSNumber numberWithInt:arg];
}
You could use the argument in some calculation to determine some property of the object returned. You could process the argument and return an object indicating the status of the calculation. And so on and so on. There's a practically unlimited range of ways you could return an object from a method. All it requires is that some object be created or accessed and then returned.
The above method returns a pointer to arg which is of type ClassName*.
I assume explaining the question would assume basic knowledge of how functions are called, how passed values are pushed on stack before function call and how return values is returned from a function.
In this specific case your arg variable is part of a class, meaning that it is stored in memory that is part of the object. When you return pointer to it you are pointing to a specific area of memory within the object.
Another option is to return copy of the value. It would mean make a copy and return it.
The difference is that if you return pointer to objects internal variable that object state could be modified from outside.
If you return copy that copy can be modified and the original object will not change.
Not sure if that helps, but you are asking about very basic software development topic which assumes some background knowledge.
Maybe specify what exactly you are looking for?
Think of methods like they are functions in math. In math, sin(180) is equal to 0. sin is the method, 180 is the argument and 0 is the return value of the method. An example of sin in objective-c might go like this:
-(double) sin:(double)angleInDegrees;
{
double sinValue;
//calculate the return value here and store it in sinValue.
//for example, if angleInDegrees is 180, then set sinValue to 0
return sinValue;
}
Returning objects is exactly the same. Look at this example:
-(NSString*) sayHelloTo:(NSString*)name;
{
return [NSString stringWithFormat:#"Hello %#!", name];
}
If I were to write it like a math function, then sayHelloTo(#"Tom") is equal to #"Hello Tom!". The only difference is that #"Hello Tom!" is an NSString object, not a double.