Пример кода: почему я все еще могу получить доступ к этому объекту 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;
}

После запуска кода и вызова этого метода я замечаю несколько вещей:

  1. Обычно, если я пытаюсь получить доступ к чему-то, что предположительно освобождено после нажатия нулевого числа удержаний, я получаю ошибку EXC_BAD_ACCESS. Здесь вместо этого я получаю ошибку malloc "double free". Почему это?

  2. Независимо от того, сколько строк "[stringPointer release] " я добавляю в код, NSLog сообщает о количестве сохранений 1. Когда я добавляю больше релизов, я просто получаю больше "двойных бесплатных" ошибок. Почему заявления о выпуске не работают так, как ожидалось?

  3. Хотя я переиздал stringPointer и получил кучу" двойных свободных " ошибок, возвращаемое значение по-прежнему работает как ни в чем не бывало (у меня есть еще один NSLog в основном коде, который сообщает возвращаемое значение). То программа продолжает работать в обычном режиме. Опять же, может ли кто-то объяснить, почему это происходит?

Эти примеры довольно тривиальны, но я пытаюсь получить полное представление о том, что происходит. Спасибо!
3 3

3 ответа:

Вы получаете двойную свободную ошибку, потому что вы выпускаете дважды и вызываете два сообщения об освобождении. =P

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

Итак опять же: освобождение (и освобождение) не требует немедленного уничтожения данных на байтовом уровне. Это просто маркер для ядра.

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

В конкретном случае NSString это кластер классов, что означает, что фактический класс, который вы получаете от alloc/init, является каким-то конкретным подклассом NSString, а не экземпляром NSString. Для " константы" strings, это чрезвычайно легкая структура,которая просто поддерживает указатель на константу C-string. Независимо от того, сколько копий этого стринга вы делаете или сколько раз вы его выпускаете, вы не повлияете на действительность указателя на строку константы C.

Попробуйте исследовать [stringPointer class] в этом случае, а также в случае изменяемой строки или форматированной строки, которая фактически использует символ формата и аргументы. Вероятно, у всех троих получится по-разному занятия.

RetainCount всегда печатает один, вероятно, вызвано оптимизацией-когда релиз замечает, что его собираются освободить, нет никакой причины обновлять retainCount до нуля (поскольку в этот момент никто не должен иметь ссылку на объект) и вместо обновления retainCount просто освобождает его.