Два Вопроса Об Управлении Памятью Objective-C: Всегда Ли Правильно Обнаружение Утечек? и почему авторелиз работает, но не выпускает?


Я только что обнаружил следующее: Как я и ожидал, освобождение объекта до его возвращения приведет к аварийному завершению работы приложения:

   + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    [expressionDescription release];
    return expressionDescription;
}

Однако я не ожидал, что следующее вызовет утечку памяти:

    + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    return expressionDescription;
    [expressionDescription release];
}

В конечном счете, моим решением было сделать это, а не:

    + (NSString *)descriptionOfExpression:(NSArray *)anExpression {
    NSMutableString *expressionDescription;
    expressionDescription = [[NSMutableString alloc] init];  
    for (id object in anExpression) {
    //Do stuff to expressionDescription
    }

    [expressionDescription autorelease];
    return expressionDescription;
}

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

Мой второй вопрос очень связан: являются ли системы обнаружения утечек памяти всегда прав?

Я понимаю, что программисты, которые разрабатывали инструменты и функцию сборки и анализа XCode, гораздо более опытны в этом вопросе, чем я, поэтому сейчас я буду считать, что они всегда правы. Однако мне трудно понять, как такая программа, как Instruments, может "знать", что происходит утечка памяти. Я думаю, что это должно полностью зависеть от того, как долго я, программист, хочу использовать объект.

Это мое понимание того, что такое "утечка" это:

Человеческое определение: память утекает, если у меня есть память, выделенная, когда я ее не использую.

Определение программирования с использованием счетчиков: утечка памяти происходит, когда выделяется память, но нет активных объектов с сохранением количества на рассматриваемом объекте.

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

3 ответа:

Проблема со вторым блоком заключается в том, что код не запускается после возврата. Я ожидал, что Xcode предупредит вас об этом (посмотрите и попробуйте исправить ваши предупреждения, а также ошибки)

Ваше понимание утечек верно. Построение и анализ могут быть обмануты - это зависит от соблюдения соглашений о кодировании. Если вы отклонитесь от этого, B&A не будет знать (или будет отмечать утечки, которые не являются правдой).

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

Возвращая выделенный объект, выполните одно из следующих действий

  1. Вызовите авторелиз на нем, прежде чем вернуть его-в этом случае вызывающий отвечает за сохранение его, если он хочет его дольше. Объект autoreleased это выпущенный после того, как весь callstack будет размотан назад к вызову iOS, который вызвал вас-вот где "пул сливается" - вы должны сохранить его перед возвращением обратно в iOS.

  2. Назовите ваше сообщение allocSOMETHING или newSOMETHING и не вызывайте авторелиз. В этом случае подразумевается, что ваше сообщение возвращает объект с числом удержаний, равным единице, и вызывающий объект отвечает за его освобождение (или авторелизинг).

Если вы сделаете одно из этих действий, Построение и анализ помогут вам понять и сделать все правильно.

EDIT: добавлено что-то новое на основе комментария

Для первой части вашего вопроса ответ основан на том, когда авторелиз фактически освобождает объект.

  • первый пример освобождает объект, прежде чем он будет возвращен и использован. Вы получаете сбой, потому что память больше не существует.
  • второй пример никогда не освобождает память, return заставляет элемент управления покинуть метод. Поэтому ваш призыв к освобождению находится в "ничейной стране" кода, он никогда не выполняется.
  • ваш последний пример работает, потому что авторелиз добавляет объект в список освобождаемых объектов (называемый пулом авторелиза). Когда пул сливается, все объекты в нем отправляются сообщение об освобождении. Стандартный пул сливается, когда цикл событий завершается, по сути, после того, как ваши методы передали управление обратно в ОС. Из-за этого он находится рядом, пока вы хотите его использовать, но удаляется автоматически в долгосрочной перспективе.

Вторая часть вашего вопроса имеет своего рода вариативный ответ. Насколько я знаю, утечка системы обнаружения не всегда верны, потому что по крайней мере часть из них основана на эвристике. Тем не менее, обнаружение утечки в приборах ошибается в сторону осторожности, поэтому вы можете быть на 99% уверены, что если он сообщает об утечке, то у вас действительно есть утечка.

Способ, которым они работают, по существу, состоит в том, чтобы отслеживать, на какие объекты на самом деле указывают. Если он находит объект, на который ничего не указывает, но который не был освобожден, то он классифицируется как утечка.

Являются ли системы обнаружения утечек памяти всегда прав?

Нет. Я дал аналогичный ответ на другой вопрос: инструмент утечки очень консервативен. Все, что он сообщает как утечка, на самом деле является утечкой, но он не обязательно сообщает обо всех утечках. Статический анализатор полагается на знание правил о том, как работают API Cocoa/Cocoa Touch, поэтому он не всегда может сделать это правильно. Например, он не знает, что -[NSTimer invalidate] освобождает приемник, потому что это не часть общие соглашения API.

С другой стороны, инструмент зомби (вы не можете, AFAICT, использовать его с приложениями iOS) логически совершенен: любая попытка доступа к объекту зомби будет замечена (это определило бы проблему, которую вы выразили выше). Однако он поставляется с большой дозой эффекта наблюдателя.