NSTimer selector method not getting accessed from another class - objective-c

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

Related

Use of undeclared identifier in my Objective-C code

I'm a newbie to Objective-C and I'm trying to a access a global variable and I can not. What have I missed?
This is the variable: getShoppingCartRequestSuccessful
-(void)setShoppingCartGetRequestWithNetworkManager//watch the bag button->4 {
//Sets the loading indicator on. [self setLoadingIndicator];
//NetworkManager [[NetworkManager sharedManager] setDelegate:self];
//GetShoppingCart [[NetworkManager sharedManager]
requestGetShoppingCartWithParameters:[[ServerRequestBuilder sharedManager]
returnParametersDictionaryForGetShoppingCartWithCustomerID:strCustomerID]];
//update shopping cart-getShoppingCartRequestSuccessful [[GlobalVariables sharedManager] setObject:shoppingCartSummary
forKey:GLOBALVARIABLES_SHOPPINGCARTSUMMARY overwrite:YES];
}
This is the argument I want to pass:
-(void)getShoppingCartRequestSuccessful:(MNShoppingCart *)shoppingCart withOpertaion:(AFHTTPRequestOperation *)operation
{
if (shoppingCart) {
MNShoppingCartSummary *shoppingCartSummary = [[MNShoppingCartSummary alloc] initShoppingCartSummaryWithCustomerID:[[shoppingCart getCustomerInfoModel] getCustomerInfoID] andWithTotalAmountOfProducts:[shoppingCart getAmountOfProducts] andWithTotalPrice:[shoppingCart getTotalPrice]];
[[GlobalVariables sharedManager] setObject:shoppingCartSummary forKey:GLOBALVARIABLES_SHOPPINGCARTSUMMARY overwrite:YES];
NSLog(#"price val call Func #6");
[self setShoppingCartScreenWithShoppingCartModel:shoppingCart withDeliveryAreaChange:YES];
}
getShoppingCartRequestSuccessful is an instance method. Not a variable. Instance method is basically a "function" that can return something or even nothing (void) to the caller. In your case, is returning a void.
When you see instance method, like this
-(void)goDoSomething:(NSString *)name
To call it, if the method is in the same Viewcontroller (ie. Class), then you do it like this:
[self goDoSomething:#"Moran"];
As you can see, goDoSomething expects ONE parameter of type NSString, so you have to give it that. This will execute the method.
In your case,
-(void)getShoppingCartRequestSuccessful:(MNShoppingCart *)shoppingCart withOpertaion:(AFHTTPRequestOperation *)operation
means, you need to pass 2 parameters to it.
To use this method, you call:
[self getShoppingCartRequestSuccessful:yourCart withOpertaion:operation];
you need to have the yourCart and operation ready before passing it to this method.
ps. withOpertaion is wrong spelling.

can an (IBAction) method be sent a message?

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.

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:)];

Objective-C invalidate timer in another method

I have a method in which i have declared a timer;
- (void)startTimer:(id)sender {
NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval: 0.8
target: self
selector: #selector(toggleButtonImage:)
userInfo: nil
repeats: YES];
}
What i want to do is, in another method i want to invalidate the timer if it is running, here's the what i have so far but i get the error 'timer is undeclared'
- (void)stopTimer:(id)sender {
if ( [timer isValid]) {
[timer invalidate], timer=nil;
}
}
Could anyone help me?
If both methods are on the same controller, then simply make the timer an instance variable. If they are not on the same object, you should rethink your design as two classes are trying to manage the same facility.

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.