Пример кода: Почему я все еще могу получить доступ к этому объекту NSString после того, как я его выпустил? - PullRequest
3 голосов
/ 07 октября 2009

Я просто писал пробный код, чтобы укрепить мое понимание 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 ]

6 голосов
/ 07 октября 2009

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

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

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

3 голосов
/ 07 октября 2009

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

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

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

0 голосов
/ 07 октября 2009

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

...