I'm attempting to use a selector with arguments and failing while doing so. I'm coming from C/++ and selectors are a tad bit confusing. I have this code:
playItem = [CCMenuItemLabel itemWithLabel:playLabel target:self selector:#selector(goToScene:)argumentHere];
How would I go about passing an argument to a method in this way?
Thanks in advance:D
You can't. Selectors specify only method to be invoked, not parameters to be passed.
What you can do, is to check sender parameter in your goToScene: method. It's gonna be the element on which action is performed (most probably CCMenuItemLabel in your case).
Thus, you can see which element was invoked (if you use goToScene: for several ui elements) and decide which 'parameter' to use.
To tell different ui elements apart, tag attribute is often used. So, code could look like
if ([sender tag] == 1) {
...
} else if ...
If you don't like too many ifs, lookup table will work.
Are you maybe looking for performSelector:withObject? I'm afraid I don't quite understand your questions maybe.
Nikita is right, when you are setting up the selector you just pass in the descriptor name. Later in your code, when you call the method, you will pass in any arguments.
Related
I saw someone passing arguments by a button's tag as follow.
how could this work? and is it safe to do like this ?
NSCustomClass* cls = [[NSCustomClass alloc] init:#"",#"",#""];
[button setTag:(int) cls];
- (void)OnClick:(id)sender
{
NSCustomClass* cls = (NSCustomClass*)[sender tag];
// to do something with the "cls".
[cls release];
}
In fact,I didn't get weird results.
It works fine by passing arguments like 'NSString','NSArray' in this way.
Can you tell me why it is a bad idea?
Casting a custom class to an int is a very bad idea and you'll get weird results.
The tag is an integer property on all UI elements. It is declared as such on UIView:
#property(nonatomic) NSInteger tag;
You can assign any integer value to it, including any predefined constants:
#define Button1Constant 1
#define PushButtonConstant 2
// …Code
[button setTag:PushButtonConstant];
// …More code
if (sender.tag == PushButtonContent)
// Handle
In general you never want to abuse the frameworks. The tag is intended to store an integer and is used mainly to access a view with viewWithTag:, which can be useful in some cases if your view was built in Interface Builder but a referencing IBOutlet is inappropriate. Stuffing a pointer into an int can give unpredictable results, as others have indicated.
On top of that there's a memory management issue. Even if the pointer survives the cast you aren't retaining the object being pointed to. This in effect is a weak pointer but without any management by the run-time. Attempts to use the pointer will likely lead to the dreaded EXC_BAD_ACCESS. And who knows what ARC might do with this mess - blow up, I would expect.
To attach data to a button (which in and of itself sounds like a possible design flaw) you should either subclass or leverage the power of the run-time with objc_setAssociatedObject() and friends.
In general, casting from a pointer type to an integer type usually indicates a design flaw; there are very few legitimate reasons to do this, and I would strongly discourage doing it, especially in this case. It may work, but it's not guaranteed to work, simply because the tag property is not documented to be used this way. The more “hacks” that you put in your code, the harder it is to maintain.
Don't learn from the code where you saw this, instead stick to reputable tutorials and books.
As a side note, it is conventional in Objective-C to name all methods starting with a lowercase letter, so the method should be onClick: and not OnClick:. The exception to this rule is when the method starts with an acronym, for example -[NSString UTF8String].
You could subclass from UIButton and define a property by yourself, instead of using its tag property.
I have a method called Display. Can somebody explain me the difference of calling the same method in the following two ways.
[self Display];
[self performselector:#selector(Display)]
- (void)Display {
NSlog(#"Data");
}
both are basically the same with one minute difference.. #selector gives a name to your method which you can pass around as an attribute to other objects or in other function calls.
Like if you want to send a message to other object and you want to send display as an attribute then you will have to give it a name using #selector and thus you can send it.. its a pretty vague concept.. hope this helps.
and to quote apple documents...
"However, the performSelector: method allows you to send messages that
aren’t determined until runtime. A variable selector can be passed as
the argument:
SEL myMethod = findTheAppropriateSelectorForTheCurrentSituation();
[anObject performSelector:myMethod];
The aSelector argument should identify a method that takes no
arguments. For methods that return anything other than an object, use
NSInvocation."
[self Display] is shorter and easier to read, write and comprehend.
[self performSelector:#selector(Display)] makes it possible to execute arbitrary selectors. If you save the selector in a variable, then you can execute it later on without knowing the method you invoke. It is therefore more flexible. Even better: you can pass selectors and objects to other objects and let them invoke it for you when necessary. An example why you want to use this is the NSUndoManager which simple invokes a selector to undo an action if the user executes the Undo command.
I do not think that there is a big difference between examples you provided, but perform selector is very useful when you for instance wanna move execution of your method to the background thread.
[self Display]; is a call to a known method on a known object.
It's easy to give it some params if your want : [self DisplayWithParam1:(NSString*)aString param2:(int)aNumber param3:(NSDictionary*)aDict
[self performselector:#selector(Display)] is a call that allows you to call a possibly not known method on a possibly not known object type.
Let's imagine you have many kind of classes that all respond to a given protocol that require to have the Display method implemented. You put some objects of thoses different classes in an NSMutableArray. When parsing the array later, you will get id typed objects.
So calling[myArrayObject Display]; will work at runtime but will generate a warning at compile time as id does not support any method of course, even if you know that this object supports the method.
To prevent thoses warning, call [myArrayObject performselector:#selector(Display)];.
The problem with that call is that is harder to pass some parameters.
performSelector:withObject:withObject:
Sends a message to the receiver with two objects as arguments.
- (id)performSelector:(SEL)aSelector withObject:(id)anObject withObject:(id)anotherObject
Parameters
aSelector
A selector identifying the message to send. If aSelector is NULL, an NSInvalidArgumentException is raised.
anObject
An object that is the first argument of the message.
anotherObject
An object that is the second argument of the message
Return Value
An object that is the result of the message.
Discussion
This method is the same as performSelector: except that you can supply two arguments for aSelector. aSelector should identify a method that can take two arguments of type id. For methods with other argument types and return values, use NSInvocation.
Availability
Available in Mac OS X v10.0 and later.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocSelectors.html
The #select call is faster. Generally the uglier (and less dynamic) the code you have in Objective-C, the faster it runs. Here, the selector call bypasses the usual call to objc_msgSend().
I wouldn't recommend writing code like this if you can avoid it. Selectors are somewhat common in Cocoa, but if you're using it for a speedup it's really not worth it. objc_msgSend() is highly optimized and very fast.
I want to do performSelector:withObject: but where the object is a CGFloat. So it's not actually an object. How can I do this?
the object I am performing the selector on is not mine, I can't modify it.
eg
[xyz performSelector:#selector(setOffset:) withObject:2];
(after posting I changed what I need to slightly to this:
[xyz performSelector:#selector(setOffset:) withObject:CGSizeMake(2,0)];
If you're trying to invoke an arbitrary selector against an object you don't have control over, you could use an NSInvocation to set up the selector, target, and arguments, and obtain the return value after the method has been executed.
Generally, though, there are simpler solutions.
try use IMP (A pointer to the start of a method implementation.)
SEL setOffsetSEL = #selector(setOffset:);
IMP setOffsetIMP = [XYZClass instanceMethodForSelector:setOffsetSEL];
setOffsetIMP(xyz, setOffsetSEL, 2);
You need an object to message. When I've needed to do something like this, I'll create a simple container class, shove the data in an instance and then perform a selector (often #selector(doIt:)) when needed.
If you can target 4.x, you can use blocks for this, too, typically.
(Without knowing more about what exactly you are trying to do, hard to get any more specific.)
object passed is a CGFloat. So it's not actually an object.
As you wrote immediately after, if you're passed an object, it can't be a CGFloat, as CGFloat is a typedef'ed primitive (float or double).
If you've been passed a number value as an object, likely you were passed an NSNumber somehow.
With zero context to your question, there's no way to be sure.
You can use:
[NSNumber numberWithFloat:(float)value]
I have a method where I do some startup animations. The method gets called many times during usage of the app, but on it's first call it needs to do some special things in addition.
Are Singletons the way to go? Maybe there is also an better way, instead of measuring how many times this method was called, and storing that in an ivar.
- (void)someMethod {
static BOOL hasBeenCalledBefore = NO;
if (!hasBeenCalledBefore) {
// perform setup
hasBeenCalledBefore = YES;
}
// do other stuff
}
Extra work may be required if you're using threads, but that's the basic idea.
Why isn't that initialization code in the constructor? Maybe you need to factor that method out into it's own class which uses the constructor to handle the init block you mention.
An amendment to chuck's answer (pretty much correct)
His works and answers your question, but another option you could use (assuming it didn't need access to any of the variables being passed to that method) would be to take the code out of your method and put it in a static initializer. It will only be executed when the class is first loaded and will isolate what is essentially completely different pieces of code.
If you want it called for every new class, use Chuck's answer but with a member variable, or use a class initializer.
A typical call to performSelectorOnMainThread: looks like this:
[target performSelectorOnMainThread:action withObject:foo waitUntilDone:NO];
where "result" is an argument passed to "action". A corresponding action would be:
- (void)doSomethingWithThing1:(id *)thing1
What is the correct syntax for calling an action that takes > 1 argument? Such as:
- (void)doSomethingWithThing1:(id *)thing1 andThing2(id *)thing2 andAlsoThing3(id *)thing3
[target performSelectorOnMainThread:action withObject:??? waitUntilDone:NO];
You can do it by putting your args in a dictionary or array and passing that to a special function
- (void)doStuff:(NSString *)arg1 and:(NSString *)arg2 and:(NSString *)arg3 {
...
}
- (void)doStuff:(NSArray *)argArray {
[self doStuff:[argArray objectAtIndex:0]
and:[argArray objectAtIndex:1]
and:[argArray objectAtIndex:2];
}
In response to a similar question on passing non-objects to a method in performSelectorOnMainThread:, I pointed out Dave Dribin's category on NSObject, which lets you do something like the following:
[[person dd_invokeOnMainThread] doSomethingWithThing1:thing1 andThing2:thing2 andAlsoThing3:thing3];
for performing your multi-argument method on the main thread. I think this is a pretty elegant solution. Behind the scenes, he wraps things in an NSInvocation, invoking that on the main thread.
The Amber framework does something similar to this, as well.
If you wish to preserve the method signature of the receiver then I think you'll need to look at using NSInvocation which allows you to specify multiple argument values.
You could wrap your call and use a dictionary as a container for your arguments as suggested in another answer but to me this seems like a bit of a code smell.
A better solution along this line would be to create a class that encapsulates the argument values - i.e. a strongly typed approach. So for example instead of passing firstname, surname, you'd pass an instance of a Person class. This is probably a better route to go down because methods with fewer arguments can yield cleaner code - but that's a whole other story.