Сообщение отправлено нераспределенному экземпляру ... Насколько я могу судить, нет причин его удалять - PullRequest
2 голосов
/ 22 июня 2011

Я работаю над приложением Iphone, которое использует базу данных SQLite в качестве основного источника данных модели.

Когда приложение открывается, одноэлементный объект «Модель» сканирует таблицу SQLite, использует каждую строку для создания объекта «Результат» (подкласс NSObject), а затем добавляет каждый результат в 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 начинается с счетчика сохранения 0 после открывающего оператора if / then и счетчика сохранения 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];
}

Моя проблема в том, что каждый раз, когда вызывается этот метод, и моя модель пытается получить значение floatValue свойства NSNumber *, я получаю сбой (используя точки останова, я выделил первую строку 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 голосов
/ 22 июня 2011

Во-первых, никогда не полагайтесь на -retainCount, так как система может творить на нем магию.

Во-вторых, randomResult возвращается из resultArray, поэтому владелец randomResult являетсяresultArray, вы не знаете, когда владелец освободит своего участника.Чтобы владеть объектом самостоятельно, вы должны отправить ему сообщение -retain, поэтому измените его на

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

Должно быть в порядке.Попробуйте.

...