Arguments with NSTimers - iphone

Is it possible to give an argument in a method when setting a NSTimer? I want to create something like the following:
[NSTimer [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(moveParticle:imageView) userInfo:nil repeats:YES];
Where "imageView" is the argument of the method. It gives me a error saying the it's expecting a semi-colon right after the parathesis after "imageView".
Any help?

You want to use the userInfo to send arguments. Look at the documentation on how to use it. You will just make your function take a single NSTimer argument then the timer will return itself and you can read its userInfo dictionary.

That's what the userInfo parameter is for. You can pass your imageView as userInfo and cast it to the desired type (NSView?) in the method you provide as selector.
e.g.:
- (void)moveParticle:(NSTimer*)theTimer
{
NSView* imageView = (NSView*)[theTimer userInfo);
...
}
Another approach (probably more useful here - as your target is self), would be to make the imageView an iVar and access that within moveParticle.

See duplicate thread :
You'll need to used +[NSTimer scheduledTimerWithTimeInterval:invocation:repeats:] instead. By default, the selector used to fire a timer takes one parameter. If you need something other than that, you have to create an NSInvocation object, which the timer will use instead.
An example :
NSMethodSignature * mSig = [NSMutableArray instanceMethodSignatureForSelector:#selector(moveParticle:)];
NSInvocation * myInvocation = [NSInvocation invocationWithMethodSignature:mSig];
[myInvocation setTarget:myArray];
[myInvocation setSelector:#selector(moveParticle:)];
[myInvocation setArgument:&imageView atIndex:2]; // Index 2 because first two arguments are hidden arguments (self and _cmd). The argument has to be a pointer, so don't forget the ampersand!
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 invocation:myInvocation repeats:true];

Related

Selector with argument

I have a method like this:
- (void)methodWithParameter:(id)parameter {
}
and I want to call it using an UIBarButtonItem
barButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:#selector(methodWithParameter:)];
I want to specify the parameter but I can't use withObject: after action: because I get a warning:
No -initWithBarButtonSystemItem:target:action:withObject: method found
can anybody help me with this?
It doesn't work that way. You cannot pass a parameter to an action. An action method will always have either:
no arguments at all,
one argument (id)sender,
or two arguments (id)sender and (UIEvent *)event.

How to pass paramerters to custom selector from a NSTimer object?

How to use the userInfo object in an NSTimer call such as
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
I want to send certain parameters to my custom selector.
The userInfo parameter is for just that.
- (void)onTimer:(NSTimer *)timer
{
NSLog(#"User Info %#", [timer userInfo] );
}
The difficulty is that you need to wrap your parameters, even if more than 1, even if they aren't objects, into a single object. Creating a temporary NSDictionary, and stuffing it with keyed parameters works. Or you could create an custom class just to hold the required parameters, and create and fill an object of that class (alloc, initWithMy42Parameters:) to pass as the userInfo.

perform:#selector using a method with parameters

I have a method hideButton
-(void) hideButton:(UIButton) *button {
[button setHidden:YES];
}
and I get a "can not use an object as parameter to a method" error.
I want to be able to give the button as a parameter to the method when calling this
[self performSelector:#selector(hideButton:smallestMonster1)
withObject:nil afterDelay:1.0];
How can this be done? as the above attempt doesnt work. I need to be able to give the button as a parameter or at least make the method aware of which button is calling to be hidden after 1 second.
Thanks
You can pass parameter to selector via withObject parameter:
[self performSelector:#selector(hideButton:) withObject:smallestMonster1 afterDelay:1.0];
Note that you can pass at most 1 parameter this way. If you need to pass more parameters you will need to use NSInvocation class for that.
Edit: Correct method declaration:
-(void) hideButton:(UIButton*) button
You must put parameter type inside (). Your hideButton method receives pointer to UIButton, so you should put UIButton* there

NSInvocation making app crash

I'm using NSInvocation as follows:
In my init, I'm writing this in my viewDidLoad:
SEL mySelector;
mySelector = #selector(initParsersetId:type:);
NSMethodSignature * sig = nil;
sig = [[self class] instanceMethodSignatureForSelector:mySelector];
myInvocation = nil;
myInvocation = [NSInvocation invocationWithMethodSignature:sig];
[myInvocation setTarget:self];
[myInvocation setSelector:mySelector];
And I'm calling it like this:
Idea *tempIdea = [[Idea alloc]init];
tempIdea = [genericArray objectAtIndex:indexPath.row];
idea.ideaId = tempIdea.ideaId;
[tempIdea release];
NSNumber *_id_ = [NSNumber numberWithInt:idea.ideaId];
[myInvocation setArgument:_id_ atIndex:2]; //CRASHING AT THIS LINE
My application is crashing at the indicated line. Can anybody please help me?
It is not very clear from your codes; however, I see something suspicious. Hopefully, it may provide your some helpful hints.
First, I don't see you retain the instance (auto released from [NSInvocation...). Since the instance from [NSInvocation...] is auto-released, your class level variable myInvocation would not retain it after viewDidLoad event.
Second thing in your codes is that the selector is a kind of customized constructor, beginning with init..... I am not sure if you can invoke the event within the same instance. Another point is that if your init... method to be invoked returns self? It should be.
You may output some messages in your selector event by using NSLog function. All the messages by NSLog will be in your XCode's output console.
I've found the answer but I'm not convinced how. Actually, initially I was writing all the initialisation code in viewDidLoad and simply reusing the NSInvocation object by passing it the different argument since NSInvocation is a mutable object. It didn't work. Then I wrote a method with all the initialisation code inside it and called that method every time I used the NSInvocation object and it worked...
You need to give setArgument: the address of the argument you are passing, and not the argument itself:
[myInvocation setArgument:&_id_ atIndex:2];
NOT
[myInvocation setArgument:_id_ atIndex:2];
Also, are you sure your function takes an NSNumber as first argument?

delegates and multiple methods

I have a problem that I solved using delegates, but now I am thinking I may have made a mistake.
This is what I want to do.
I have a class that runs on a delay. When it is done it has a finished delegate that it calls.
Now I have the main class that creates two of these delay classes.
I don't want them to both be handled by the same isfinished method in the main class. I want to use two different ones.
However I believe with the protocol method of creating delegates that this will not work for me.
Is there a way around this?
delayclass setdelegates MainclassFunction1
delayclass setdelegates MainclassFunction2
If I understand you correctly, take a look at the NSTableViewDelegate protocol. There, each delegate method's first argument is the NSTableView instance sending the message.
You can solve your issue by changing your delegate methods to have your delegating object send itself as an argument. Then, in your delegate, you'd do something like this:
if (theDelegator == objectA)
{
// Do something
}
if (theDelegator == objectB)
{
// Do something else
}
This way, you've got one cleanly-implemented delegate method that can handle multiple objects delegating to it.
Using delegates doesn't seem like the correct approach to me; they're generally used for augmenting behavior. What sounds most appropriate here is the target/selector pattern, like NSTimer.
#interface MyObject : NSObject {
#private
id target;
SEL selector;
}
#property(assign) id target;
#property SEL selector; /* The selector must return void and accept one argument, which is the MyObject instance that invoked the method. */
#end
#implementation MyObject
- (void)notifyTarget {
[[self target] performSelector:[self selector] withObject:self];
}
#synthesize target;
#synthesize selector;
#end
This is generally the cleanest approach since the delegate callback doesn't need to disambiguate the sender. Using notifications seems like too much overhead for a problem in this domain.
As mentioned, commonly delegate methods would include the object initiating the callback, so you can differentiate that way. Alternately you could have the object post a notification instead, which will also make the originator available.
Why are you not just using NSTimer, adding different timers and having them call whatever selectors you like in the class you are using as a delegate now?
Something like:
NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:#selector(myMethod1:) userInfo:nil repeats:YES];
NSTimer *timer2 = [NSTimer scheduledTimerWithTimeInterval:0.5f target:self selector:#selector(myMethod2:) userInfo:nil repeats:YES];
Where your methods are:
- (void) myMethod1:(NSTimer*)theTimer
{
// do Something
}
- (void) myMethod2:(NSTimer*)theTimer
{
// do Something different
}
You want to save off and retain both timer1/timer2 references, so that you can stop the timers in dealloc ([timer1 invalidate]).
Short note: Generally, it's bad style to have "if" statements that switch on an object. We all do it occasionally for getting that second list w/o needing a new controller, but switching is what method calls do internally, so ideally you'd just let the ObjC runtime take care of doing the right thing. Several options:
-(void) tableViewSelectionDidChange: (NSTableView*)theView
{
SEL theAction = NSSelectorFromString( [NSString stringWithFormat: #"tableView%#SelectionDidChange:", [theView autosaveName]] );
[self performSelector: theAction withObject: theView];
}
-(void) tableViewUKSourceListSelectionDidChange: (NSTableView*)theView
{
// UKSourceList-table-specific stuff here.
}
-(void) tableViewUKUsersListSelectionDidChange: (NSTableView*)theView
{
// UKUsersList-table-specific stuff here.
}
This works best when you have a non-localized string label, like the autoSave name, but can also use the tag, although that makes the code less readable (which one is "table 1"?). Sometimes it's better to just write a subclass that has a special string for that purpose, or even has methods where you can specify selector names to forward the delegate methods to.
Caleb's suggestion is also good, it's also called "target/action" in case you want to google for it. I have several (Mac) classes that have a regular "action" for clicks, a "doubleAction" for double clicks etc.