消息发送到解除分配的实例......据我所知,没有理由将其解除分配

时间:2011-06-22 05:28:44

标签: iphone ios memory-management nstimer dealloc

我正在开发一个使用SQLite数据库作为模型数据主要来源的Iphone应用程序。

当应用程序打开时,名为“Model”的单例对象扫描SQLite表,使用每一行创建一个“Result”对象(一个NSObject子类),然后将每个Result添加到NSMutableArray。

以下是该部分的代码,从我找到数据库的路径后开始:

- (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 numberWithFloat:(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");
}

稍后,我的一个视图控制器告诉我的模型启动一个重复的NSTimer,并触发一个方法从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];
}

-(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;
}

控制台读出告诉我在此方法过程中保留计数是正常的... randomResult在打开if / then语句后以保留计数0开始,并且在保留计数为1之后已经归还了。

然后我的模型根据其他模型数据应用一些计算来生成格式化的结果字符串,以便在视图中显示:

-(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];
}

我的问题是,每次调用此方法,并且我的模型试图获取NSNumber * cost属性的floatValue时,我都会崩溃(使用断点,我隔离了 calculateQuantity的第一行:作为崩溃点),我在控制台中收到以下消息:

- [CFNumber floatValue]:发送到解除分配的实例0x6332280的消息

这种方法的工作方式是 [sharedModel calculateQuantity]; 在计时器的第一个刻度上调用,然后崩溃:

-(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];
}

我不相信我两次打电话,我的记忆管理看起来很好。

最令人困惑的部分是,如果我将 calculateQuantity 方法的前几行移动到 randomResult 方法的末尾,我仍然会遇到崩溃。即使在控制台刚刚告诉我保留计数为1之后,它立即说我正在尝试向已解除分配的实例发送消息。

我知道这看起来像是一个很多问题但我无法从任何其他线程中找出问题。

1 个答案:

答案 0 :(得分:0)

首先,永远不要依赖-retainCount因为系统可能会对它做一些魔法。

其次,randomResultresultArray返回,因此randomResult的所有者为resultArray,您不知道所有者何时释放其成员。要自己拥有一个对象,您应该发送一条-retain消息,然后将其更改为

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

应该没问题。试一试。