views:

65

answers:

2

I am trying to run a method from my app delegate, that saves an object in other class as well as invalidates a timer. I have it set up so that when my app exits, it runs the method in my class and saves and stops the timer.

In app delegate:

- (void)applicationWillResignActive:(UIApplication *)application {

    // Save the mission because they are leaving the app
    if ([timeRun hasScore]) {
        [timeRun resetWithSave];
    }

}

The method it calls in the "timeRun" class:

- (void)resetWithSave {
    // Save
    self.counterInt = 150;
    self.timer.text = [self timeInSeconds:counterInt];
    [start setBackgroundImage:[UIImage imageNamed:@"play.png"] forState:UIControlStateNormal];
    self.started = NO;
    self.score.text = @"0";
    [self saveMission];
    if ([countTimer isValid]) {
        [countTimer invalidate];
    }
    [table scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:NO];
}

But, I am getting crashes:

2010-09-11 19:35:21.503 Score Card[2747:307] -[__NSCFType isValid]: unrecognized selector sent to instance 0x19e190
2010-09-11 19:35:21.594 Score Card[2747:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType isValid]: unrecognized selector sent to instance 0x19e190'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x30897ed3 __exceptionPreprocess + 114
    1   libobjc.A.dylib                     0x3002f811 objc_exception_throw + 24
    2   CoreFoundation                      0x30899683 -[NSObject(NSObject) doesNotRecognizeSelector:] + 102
    3   CoreFoundation                      0x308411d9 ___forwarding___ + 508
    4   CoreFoundation                      0x30840f90 _CF_forwarding_prep_0 + 48
    5   Score Card                          0x00006b3d -[TimeRun resetWithSave] + 272
    6   Score Card                          0x00002b39 -[Score_CardAppDelegate applicationWillResignActive:] + 80
    7   UIKit                               0x31ea6879 -[UIApplication _setActivated:] + 212
    8   UIKit                               0x31eda4ab -[UIApplication _handleApplicationSuspend:eventInfo:] + 238
    9   UIKit                               0x31eab301 -[UIApplication handleEvent:withNewEvent:] + 2200
    10  UIKit                               0x31eaa901 -[UIApplication sendEvent:] + 44
    11  UIKit                               0x31eaa337 _UIApplicationHandleEvent + 5110
    12  GraphicsServices                    0x31e4504b PurpleEventCallback + 666
    13  CoreFoundation                      0x3082cce3 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 26
    14  CoreFoundation                      0x3082cca7 __CFRunLoopDoSource1 + 166
    15  CoreFoundation                      0x3081f56d __CFRunLoopRun + 520
    16  CoreFoundation                      0x3081f277 CFRunLoopRunSpecific + 230
    17  CoreFoundation                      0x3081f17f CFRunLoopRunInMode + 58
    18  GraphicsServices                    0x31e445f3 GSEventRunModal + 114
    19  GraphicsServices                    0x31e4469f GSEventRun + 62
    20  UIKit                               0x31e51123 -[UIApplication _run] + 402
    21  UIKit                               0x31e4f12f UIApplicationMain + 670
    22  Score Card                          0x000029ef main + 70
    23  Score Card                          0x000029a4 start + 40
)
terminate called after throwing an instance of 'NSException'
Program received signal:  “SIGABRT”.
kill
quit

Why would this crash my app?

A: 

It looks like the timer was never initialized or it was released. Sometimes exceptions like that will be thrown with a random object when it is nil as well. Check your pointers and retain counts!

Justin Meiners
+2  A: 

On a hunch, you're doing something like

countTimer = [NSTimer scheduledTimerWithTarget:...];

...

if ([countTimer isValid])
{
  [countTimer invalidate];
}

...


if ([countTimer isValid])
{
  [countTimer invalidate];
}

A "scheduled" timer is added to the run loop for you. It is then retained by the run loop. Invalidating it removes it from the run loop, thus it is released. If nothing else is retaining it, it is dealloced. When you try to use it again, it crashes.

Try something like [countTimer invalidate]; countTimer = nil;

Alternatively, you can retain the timer, but note that the timer retains its target so it's easy to end up with a retain cycle.

tc.
Yah. Since you didn't retain the timer (as you generally shouldn't), you don't know when it will be released, except that it will be after it's callback method is called. You should set its reference to nil at that time, or whenever you invalidate it, because it will automatically be released after that.
Ed Marty
You know when it is released *by the run loop*. You don't know when it'll be dealloced; that's different.
tc.