开发者

Message Sent To Deallocated Instance ... As far as I can tell, there's no reason it should be deallocated

I am working on an Iphone App that uses a SQLite database as its main source of Model data.

When the app opens, the singleton object called "Model" scans the SQLite table, uses each row to create a "Result" object (an NSObject subclass), and then adds each Result to an NSMutableArray.

Here's the code for that section, starting right after I have found the path for the database:

- (void)populateResultArrayWith:(NSString *)dbPath {

const char *sql = "SELECT * FROM results";
sqlite3_stmt *selectstmt;
if(sqlite3_prepare_v2(database, sql, -1, &selectstmt, NULL) == SQLITE_OK) {

    while(sqlite3_step(selectstmt) == SQLITE_ROW) {

        NSInteger pkInt = sqlite3_column_int(selectstmt, 0);
        NSNumber *pk = [NSNumber numberWithInt:pkInt];
        NSString *v = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)];
        NSString *n = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 2)];
        NSString *s = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 3)];
        NSNumber *c = [NSNumber nu开发者_运维知识库mberWithFloat:(float)sqlite3_column_double(selectstmt, 4)];
        NSString *t1 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 5)];
        NSString *t2 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 6)];
        NSString *t3 = [NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 7)];

        Result *resultObj = [[Result alloc] initWithPrimaryKey:(NSNumber *)pk
                                                      withVerb:(NSString *)v
                                                      withNoun:(NSString *)n
                                                    withSuffix:(NSString *)s
                                                      withCost:(NSNumber *)c
                                                      withTag1:(NSString *)t1
                                                      withTag2:(NSString *)t2
                                                       andTag3:(NSString *)t3];

        [self.resultArray addObject:resultObj];
        [resultObj release];
    }
}
else
    sqlite3_close(database); //Even though the open call failed, close the database connection to release all the memory.
    NSLog(@"Model: Closing Database");
}

Later, one of my view controllers tells my Model to start a repeating NSTimer, and triggers a method to generate a random result from the NSMutableArray:

-(void)startTimer
{
if (startDate && startDate != nil) {
    startDate = nil;
}
self.modelCumulativeValueWasted = 0;
startDate = [[NSDate date] retain];
modelTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0/1.0) 
                                         target:self
                                       selector:@selector(tick) 
                                       userInfo:nil
                                             repeats:YES];
[self randomResult];
}

And:

-(Result *)randomResult
{
if (randomResult != nil) {
    randomResult=nil;
}

NSLog(@"Selected Result Retain Count At Beginning of Method = %i", [randomResult retainCount]);
// Generate a random int between 0 and the total count of the resultArray.
int randomIndex = arc4random() % [resultArray count];

// Select a result from the resultArray at that index - with an iVar.
randomResult = [resultArray objectAtIndex:randomIndex];
NSLog(@"Selected Result Retain Count At End of Method= %i", [randomResult retainCount]);

return randomResult;
}

The console read-out tells me that the retain count is normal during the course of this method... randomResult starts with a retain count of 0 after the opening if/then statement, and a retain count of 1 after it has been returned.

Then my Model applies some calculations based on other model data to generate a formatted Result string for display in the view:

-(void)calculateQuantity
{
    // Get the float of the randomly selected result's "cost" property.
    selectedCostFloat = [randomResult.cost floatValue];

    // Calculate "how many of this result item could we buy" by dividing the cumulativeValueWasted by the costFloat.
    float quantityFloat = (modelCumulativeValueWasted / selectedCostFloat);
    NSLog(@"Quantity Float = %f", quantityFloat);
    // Save this float as an NSNumber object, so a NSNumberFormatter can interpret it in ResultVC.
    quantityNumber = [NSNumber numberWithFloat:quantityFloat];
}

My problem is that every time this method is called, and my model tries to get the floatValue of the NSNumber *cost property, I get a crash (using breakpoints, I isolated the first line of calculateQuantity: as the crash-point), and I get the following message in the console:

-[CFNumber floatValue]: message sent to deallocated instance 0x6332280

The way this works is that [sharedModel calculateQuantity]; is being called on the first tick of the timer, and then it crashes:

-(void)tick
{
// For "Time" every tick
NSDate *currentDate = [NSDate date];
NSTimeInterval countInSeconds = [currentDate timeIntervalSinceDate:startDate];

[df setDateFormat:@"HH:mm:ss"];
[df setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0.0]];

NSDate *modelTimerDate = [NSDate dateWithTimeIntervalSinceReferenceDate:countInSeconds];
self.modelTimeString = [df stringFromDate:modelTimerDate];
NSLog(@"Time String: %@",self.modelTimeString);

//For "$" every tick
self.modelCumulativeValueWasted += self.modelMeetingValuePerSecond;

self.modelNumberToDisplayString = [NSString stringWithFormat:@"$%1.2f",self.modelCumulativeValueWasted];
NSLog(@"Cumulative Money Wasted: %@",self.modelNumberToDisplayString);

[self calculateQuantity];
}

I don't believe I am calling anything twice, and my memory management looks fine.

The most confusing part is that if I move the first couple lines of the calculateQuantity method to the end of the randomResult method, I still get the crash. Even after the console JUST told me that the retain count was 1, it immediately says I am trying to send a message to a deallocated instance.

I know this looks like a question that comes up a lot, but I haven't been able to figure out the problem from any of the other threads.


First, never rely on the -retainCount since the system may do some magics on it.

Second, the randomResult is return from resultArray, so the randomResult's owner is resultArray, you don't know when the owner release its member. To own a object by yourself, you should send it a -retain message, so change it to

-(Result *)randomResult
{
  ...
  randomResult = [resultArray objectAtIndex:randomIndex];    
  return [[randomResult retain] autorelease];
}

Should be OK. Give it a try.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜