Пример кода: Почему я все еще могу получить доступ к этому объекту NSString после того, как я его выпустил?
Я просто писал некоторый исследовательский код, чтобы укрепить мое понимание Objective-C, и я наткнулся на этот пример, который мне не совсем понятен. Я определяю этот метод и запускаю код:
- (NSString *)stringMethod
{
NSString *stringPointer = [[NSString alloc] initWithFormat:@"string inside stringPointer"];
[stringPointer release];
[stringPointer release];
NSLog(@"retain count of stringPointer is %i", [stringPointer retainCount]);
return stringPointer;
}
После запуска кода и вызова этого метода я заметил несколько вещей:
Обычно, если я пытаюсь получить доступ к чему-то, что предположительно освобождено после достижения нулевого счетчика, я получаю ошибку EXC_BAD_ACCESS. Здесь вместо этого я получаю ошибку malloc "double free". Это почему?
Независимо от того, сколько строк "[stringPointer release]" я добавляю в код, NSLog сообщает об оставшемся числе 1. Когда я добавляю больше выпусков, я просто получаю больше "двойных свободных" ошибок. Почему заявления о выпуске не работают должным образом?
Хотя я перевыпустил stringPointer и получил кучу ошибок "двойного освобождения", возвращаемое значение по-прежнему работает так, как будто ничего не произошло (у меня есть другой NSLog в основном коде, который сообщает возвращаемое значение). Программа продолжает работать в обычном режиме. Опять же, может кто-нибудь объяснить, почему это происходит?
Эти примеры довольно тривиальны, но я пытаюсь получить полное представление о том, что происходит. Спасибо!
3 ответа
Вы получаете двойную бесплатную ошибку, потому что вы выпускаете дважды и вызываете два сообщения Deloc. = Р
Имейте в виду, что только то, что вы освобождаете, не означает, что данные по адресу их памяти будут немедленно уничтожены. Он просто помечен как неиспользуемый, поэтому ядро знает, что в какой-то момент в будущем оно будет свободно использоваться для другого фрагмента данных. До этой точки (которая полностью недетерминирована в пространстве вашего приложения) данные будут там оставаться.
Итак, еще раз: освобождение (и удаление) не требует немедленного уничтожения данных на уровне байтов. Это просто маркер для ядра.
Здесь происходит несколько вещей. Во-первых, удаление объекта не обязательно очищает память, которую ранее занимал объект. Он просто помечается как бесплатный. Если вы не сделаете что-то еще, что приведет к повторному использованию этой памяти, старые данные будут просто зависать.
В конкретном случае NSString это кластер классов, что означает, что реальный класс, который вы получаете от alloc/init, является некоторым конкретным подклассом NSString, а не экземпляром NSString. Для "постоянных" строк это чрезвычайно легкая структура, которая просто поддерживает указатель на константу C-строки. Независимо от того, сколько копий этого страйка вы делаете или сколько раз вы его выпускаете, вы не будете влиять на достоверность указателя на константу C-строки.
Попробуйте изучить [класс stringPointer] в этом случае, а также в случае изменяемой строки или отформатированной строки, которая фактически использует символ формата и аргументы. Вероятно, у всех трех будут разные классы.
RetainCount, всегда печатающий единицу, вероятно, вызван оптимизацией - когда релиз замечает, что он будет освобожден, нет смысла обновлять retainCount до нуля (так как в этом месте никто не должен иметь ссылку на объект), и вместо обновления retainCount просто освобождает это.