Надоедливая утечка в методах установки / получения - PullRequest
3 голосов
/ 11 июля 2011

Кажется, я продолжаю задавать одни и те же вопросы, связанные с памятью.Мой текущий код работает точно так, как я намереваюсь, но я не могу понять, почему я показываю утечку здесь в Инструментах.

-(NSDate *)startTimeAndDate {
    NSDate *dateToReturn = nil;
    if (startTimeAndDate != nil) {
        dateToReturn = [startTimeAndDate retain];
    } else { //is currently nil, this will be the initial setting
        //return default time if we have a working date
        if (finishTimeAndDate != nil) {
            dateToReturn = [[self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime]retain];
        } else {
            //return the default time with today's date if we have nothing set as yet
            dateToReturn = [[self dateFromDate:[NSDate date] withNewTime:defaultStartTime] retain];
        }
        //save the initial setting
        self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
    }
    [startTimeAndDate release];
    startTimeAndDate = dateToReturn;
    return startTimeAndDate;
}


-(void)setStartTimeAndDate:(NSDate *)inStartTimeAndDate {

    BOOL initialAssignment = NO;
    if (startTimeAndDate == nil) {
        initialAssignment = YES;
    }

    if (startTimeAndDate != inStartTimeAndDate) { //skip everything if passed object is same as current
        //check that the start time is prior to finish only if finish time has been entered
        NSDate *dateToSetStartTo = nil;
        if (finishTimeAndDate != nil) {
            if ([inStartTimeAndDate earlierDate:finishTimeAndDate] == inStartTimeAndDate) {
                // use the new time, it is earlier than current finish time
                dateToSetStartTo = [inStartTimeAndDate retain];
            } else { //start time is not earlier then finish time
                // the received entry is invalid, set start time to 1 default interval from finish
                dateToSetStartTo = [[finishTimeAndDate dateByAddingTimeInterval:-self.defaultTimeInterval] retain];
            }
        } else { //finish time is nil
            // use the new time without testing, nothing else is set
            dateToSetStartTo = [inStartTimeAndDate retain];
        }
        [startTimeAndDate release];
        startTimeAndDate = dateToSetStartTo;
    }
    if (initialAssignment) {
        self.initialStartDateAndTime = [[self.startTimeAndDate copy] autorelease];
    }
}

Насколько я вижу, я балансирую все остатки с release илиautorelease.Утечка, по-видимому, вызвана только при первом проходе.У меня есть контроллер представления, он создает мою модель (в которой лежит этот код) и устанавливает дату начала, больше ничего не делается в этой точке.Если я закрою этот контроллер представления в тот момент, Instruments покажет, что я оставляю объект даты как утечку.

Я поместил NSLog, чтобы показать счетчик удержаний на dealloc, и, конечно же, онсохраните счет 2 до того, как будет назван мой последний релиз, оставив счет 1, когда он должен был быть уничтожен.Это всегда одно и то же, независимо от того, закрываюсь ли я сразу после инициализации или устанавливаю и получаю сто раз.retainCount - это 2 до моего последнего звонка на release в dealloc.

Я смотрел на это все выходные и не могу понять, где я ошибся.

Комупоясните, что первоначальный вызов должен установить свойство startTimeAndDate.В этот момент все остальные поля равны нулю или 0, если не объекты.Этот startTimeAndDate объект выглядит как протекающий объект.

Ответы [ 2 ]

4 голосов
/ 11 июля 2011

Во-первых, можете ли вы описать проблему, которую вы пытаетесь решить с помощью этого кода?Я спрашиваю, потому что это кажется очень сложным, и моя первоначальная мысль заключается в том, что упрощение не только прояснит, что вы делаете, но также, вероятно, решит и вашу утечку.

Во-вторых, (и я могу ошибаться)вам нужно только сохранять / освобождать объекты, если вы ожидаете, что эти объекты существуют вне области действия метода, или вы ожидаете, что они могут быть освобождены некоторым кодом, который вы кодируете в своем методе.Исходя из этого, вы, кажется, слишком долго сохраняете и выпускаете код.Я думаю, что вы можете удалить многое из этого.

Опять же, я могу ошибаться, но, похоже, вы действительно будете течь.Причина, по которой я так думаю, заключается в следующем: при первом проходе вы сохраняете некоторые данные в dateToReturn, который является локальной переменной.Тогда вы делаете

self.initialStartDateAndTime = [[dateToReturn copy] autorelease];

Но это не выпуск dateToReturn.Вместо этого он выпускает копию dateToReturn.dateToReturn все еще сохраняется.Предполагая, что вы намереваетесь автоматически выпустить копию, потому что initialStartDateAndTime установлен с помощью retain, я думаю, что вы должны сделать:

self.initialStartDateAndTime = [[dateToReturn copy] autorelease];
[dateToReturn release];

Конечно, если вы удалите дополнительные сохранения / выпуска, это снова станет проще.

Последнее, что я хотел бы предложить, - это назвать.Проблема с таким кодом заключается в том, что у вас есть несколько методов и переменных, все с очень похожими именами.Это может затруднить отслеживание и привести к ошибкам.Так что спросите себя, действительно ли вам нужно так много переменных.И можете ли вы сделать свой код более читабельным, изменив некоторые имена.

2 голосов
/ 13 июля 2011

Дам, не обращай внимания на то, что я сказал.Я просто снова просмотрел код, и ты прав.Я думаю, что вы в основном сгорели от сложности кода.Мне было довольно трудно следить, особенно с количеством свойств.Я думаю, что я бы сделал на этом этапе, чтобы скопировать код в модульный тест и запустить его оттуда.Тогда вы можете лучше проверить и отладить его.Я бы порекомендовал GHUnit , если у вас еще нет модульного тестирования.

Другое дело, что в вашей программе выполняется код где-то еще, который сохраняет дату,Поэтому и происходит утечка.Например, если inStartTimeAndDate поступает со счетом сохранения 1, но не освобождается кодом, который вызвал установщик, тогда вы можете получить startTimeAndDate с сохранением 2.

Сказаввот мое переписывание геттера в попытке выяснить, что происходит:

-(NSDate *)startTimeAndDate {

    // If we have it, bail out fast.
    if (startTimeAndDate == nil) {
        return startTimeAndDate;
    }

    // Is currently nil, this will be the initial setting
    NSDate *dateToReturn = nil;
    //return default time if we have a working date
    if (finishTimeAndDate != nil) {
        dateToReturn = [self dateFromDate:finishTimeAndDate withNewTime:defaultStartTime];
    } else {
        //return the default time with today's date if we have nothing set as yet
        dateToReturn = [self dateFromDate:[NSDate date] withNewTime:defaultStartTime];
    }
    //save the initial setting
    self.initialStartDateAndTime = [[dateToReturn copy] autorelease];

    startTimeAndDate = [dateToReturn retain];
    return startTimeAndDate;
}

Основная причина этого переписывания заключалась в том, что, как оказалось, если был startTimeAndDate, то код былделая это:

dateToReturn = [startTimeAndDate retain];
...
[startTimeAndDate release];
startTimeAndDate = dateToReturn;

Это казалось немного бессмысленным, потому что эффективно выполнять сохранение, освобождение и самостоятельное назначение.Это сработало бы, но вероятность ошибки была бы меньше, если бы мы ее оставили.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...