can an (IBAction) method be sent a message? - objective-c

I want to delay between each output result for say, 1 second. The very last line of the following code is throwing an error "Use of undeclared identifier 'dealCard'", but I have declared it in the header file as shown below.
- (IBAction)startPause:(id)sender
{
if ([self.deal length]>cardNum) {
timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(dealCard) userInfo:nil repeats:NO];
card.text = [NSString stringWithFormat:#"%i",cardNum];
[timer fire];
}
}
- (void) dealCard{
NSLog(#"dealCard: %d: ", cardNum+self.randCut);
cardNum=cardNum + 1;
[self startPause:(id)dealCard];
}
header file is next.
- (IBAction) startPause:(id)sender ;
- (void) dealCard;

This is just a compilation error because dealCard is not a variable in the dealCard method. You can pass a selector as an argument. That would be done like:
[self startPause:#selector(dealCard)];
or even
[self startPause:_cmd];
since _cmd gives you the current selector and you happen to be in dealCard. Note however that usually the "sender" parameter is not used for passing a selector, but an object. For example an instance of UIButton sending a message like:
- (IBAction)myButtonResponse:(id)sender;
would pass self for the sender parameter.

You can call your IBAction method with
[self startPause:nil];
since the sender parameter is never used. Remember that dealCard is a method, which has a selector associated. It's not a property (not even an object), so you can't pass it as an argument using only its name.
You have a recursive problem, too. dealCard calls startPause:, and startPause: calls dealCard again. You should have a stop condition in dealCard to end the "two level" recursive calls.

Related

Xcode Cocos 2D How To Use Selector With Multiple Parameters

I am trying to do something like this:
CCMenuItemImage *BuyButton = [CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" target:self selector:#selector(Function:cnt)];
For some reason I can't pass any parameters to the function 'Function'. I have spend a lot of time looking into this but the the only solution i have found uses object ids and i would rather not get into that. This button is in a loop so i can't just have another function called thats get parameters from elsewhere.
+ (id)itemWithNormalImage:selectedImage:target:selector: does not support selectors with parameters. If you want to perform a selector that takes arguments, you can use + (id)itemWithNormalImage:selectedImage:block: instead. In the block just run any code you want:
__weak typeof(self) weakSelf = self;
[CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" block:^(id sender) {
[weakSelf methodWithParameterOne:one two:two];
}];
You can not send parameters by selectors by colons.
The typical example is :
[self performSelector:#selector(myMethodWithObject:) withObject:myObject];
Which calls
- (void)myMethodWithObject:(id)object;
Similarly you need to do like above, may be as :
CCMenuItemImage *BuyButton = [CCMenuItemImage itemWithNormalImage:#"Buy.jpg" selectedImage:#"Buy.jpg" target:self selector:#selector(Function:) withObject:cnt];
You need to change the Function by:
-(void)FunctionWithCnt:(<type>)cntObject;
and then use #selector(FunctionWithCnt:)

NSTimer selector method not getting accessed from another class

I have one class (Sample) defined in sample.m where I have the following methods:
-(NSInteger)refreshValue:(id)sender{
//Do some thing here and return value.
NSLog(#"Print something here");
return something;
}
-(IBAction)monitorValue:(id)sender {
NSLog(#"In here");
timer = [NSTimer scheduledTimerWithTimeInterval: 1
target: self
selector: #selector(refreshValue:)
userInfo: nil
repeats: YES];
}
Now I have another class, test.m. From test.m, I do this:
Sample *test = [Sample alloc]
[test monitorValue:(id)sender]
WHen I run this code, I can see the monitor value method get called. But the refreshValue method never gets called from within monitorValue, which is what I want. If I call monitorValue from sample.m, then everything works. It just does not work when called from a different object.
Any ideas ? Thanks.
Your timer callback method signature is not correct. Per the documentation, it should be:
- (void)timerFireMethod:(NSTimer*)theTimer

Change variable value from NSTimer

I'm newbie to Cocoa/Objective C. I've to change a value of a global NSSTring variable on every iteration of an NSTimer execution. I've declared the variable inside the appdelegate.m at the top of the file so it's global:
NSString *my_string = #"hello";
I call the NSTimer:
[[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:#selector(scan:) userInfo:nil repeats:YES] fire];
and inside scan i set the new value to my_string:
- (void) scan:(NSTimer *)timer{
//some execution
my_string = #"the new value";
}
but the variable value is always the same "hello", the content won't change.
Is it possible to do this? Solutions?
You do not need to call fire method, the scheduled timer will fire automatically after the specified interval.
Also, set a breakpoint at the scan: method to find out if it is called.
If you declare your my_string variable in the .m file then other files won't be able to see it (you #import the .h files not the .m). Do you do the timer stuff in the same file (appdelegate.m)?
I recommend not having global variables like this as it will often confuse things as the project builds up. You can have it either as an ivar with an accessor, or as static in the #implementation block with a static accessor so that you can have access to a unique instance from anywhere.
You can log the change to make sure it happens or set a breakpoint.
- (void) scan:(NSTimer *)timer{
//some execution
my_string = #"the new value";
NSLog(#"Changed my_string to %#", my_string);
}

Create a SEL with an object passed into it

I know how to use:
[self method:object];
But is it possible to get a SEL object of this?
SEL method = #selector(method:object);
Doesn't work.
Thanks :)
A SEL is just the selector - the name of the message that's sent. To capture a specific instance of that message, its arguments, and its return value as an object, you need to use NSMethodSignature and NSInvocation. An example, based on your hypothetical -method:object above:
NSMethodSignature *sig = [SomeClass instanceMethodSignatureForSelector:#selector(method:)];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
// Assume that someObject is an instance of SomeClass
[inv setTarget:someObject];
// Assume an "id object" declared elsewhere.
// Also note that self & _cmd are at indices 0 & 1, respectively
[inv setArgument:&object atIndex:2]
// Some time later...
[inv invoke];
Note that, because an NSInvocation is an object, it doesn't have to be invoked immediately. It can be stored for later use, and usually is - there are far easier ways to send a message if one wants to do so immediately. Cocoa's standard undo/redo machinery, for example, is based on storing and invoking NSInvocations.
A #selector is something that is of another method or function.
Take this for an example:
-(IBAction)timerStart {
timer = [NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(targetMethod:)
userInfo:nil
repeats:NO];
}
-(void)targetMethod:(id)sender {
[timer invalidate];
timer = nil;
}
As you can see, the selector (targetMethod:) is being called to action after two seconds of the NSTimer is run. The targetMethod: is a (void)function:(id)sender and therefore that is run.
In your case, what I think you're trying to accomplish is
[self performSelector:#selector(methodName:)];

Proper way of passing a primitive argument with an NSTimer

I'm using a basic timer that calls this method:
- (void) refresh:(id)obj
{
if (obj == YES) doSomething;
}
I want to call this method from certain areas of my code and also from a timer
[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(refresh:)
userInfo:nil
repeats:YES];
When I put YES as the argument for the userInfo parameter, I get an EXC_BAD_ACCESS error; why is this?
Can someone help me do this the right way so that there is no ugly casting and such?
The userInfo parameter must be an object; it is typed id. YES is a primitive, namely the value 1. In order to make sure the userInfo object does not get deallocated, the timer retains it. So, when you passed YES, NSTimer was doing [(id)YES retain]. Try that in your own code and see what happens. :-P
As the Documentation states, the selector you give the method must have the signature
- (void)timerFireMethod:(NSTimer*)theTimer
This means you can't have an NSTimer invoke just any method—not directly at least. You can make a special method with the above signature which in turn invokes any method you want though.
So, say you have a method called refresh:, and you want to call it every so often, passing it YES. You can do this like so:
// somewhere
{
[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(invokeRefresh:)
userInfo:nil
repeats:YES];
}
- (void)invokeRefresh:(NSTimer *)timer {
[self refresh:YES];
}
- (void)refresh:(BOOL)flag {
if (flag) {
// do something
}
}
In Objective-C, a primitive type is not an object. So you can't directly pass it to an argument which expects an id, which stands for a generic object. You need to wrap it into an NSNumber object.
Use
NSTimer*timer=[NSTimer scheduledTimerWithTimeInterval:refreshInterval
target:self
selector:#selector(refresh:)
userInfo:[NSNumber numberWithBool:YES]
repeats:YES];
and
- (void) refresh:(NSTimer*)timer
{
NSNumber* shouldDoSomething=[timer userInfo];
if ([shouldDoSomething boolValue]) doSomething;
}
Don't forget to invalidate and release the timer once it's done.
By the way, you don't have to compare a BOOL (or C++ bool) against YES or true or whatever. When you write
if(a>b) { ... }
a>b evaluates to a bool, and if uses the result. What you're doing there is like
if((a>b)==YES) { ... }
which is quite strange to me. It's not that the (..) after if should contain a comparison; it should contain a bool.
As a follow-up to kperryua's answer, if you want to pass a primitive through userInfo you can box it with NSNumber or NSValue; in the case of a boolean, you'd want to use [NSNumber numberWithBool:YES], then call boolValue in the timer callback to get back the primitive.