NSString - stringWithFormat против stringWithString

У меня есть глобальная переменная NSString, объявленная в файле .m. Когда я использую stringWithFormat для присвоения ему строкового значения в любом месте файла .m, это значение не сохраняется между вызовами функций. Однако, когда я делаю то же самое, используя stringWithString, значение сохраняется на протяжении всей реализации и любых вызовов функций, что дает ему истинное глобальное поведение. Другими словами, чем отличается внутреннее управление памятью для stringWithFormat и stringWithString?

Почему значение stringWithString сохраняется при выдаче «сообщения, отправленного в освобожденный экземпляр» (при последующем использовании NSString, скажем, в инструкции NSLog в другой функции, которая идет после функции, которая дала NSString значение с помощью stringWithFormat) при использовании stringWithFormat? Нет таких проблем, когда я заменяю то же самое на stringWithString.

Спасибо!

РЕДАКТИРОВАТЬ: Я забыл упомянуть, что ДА, это сработало в случае stringWithFormat, когда я впоследствии сделал ручное сохранение. Ценность сохранилась. Отсюда вопрос.


person Bourne    schedule 22.02.2011    source источник
comment
Пожалуйста, вставьте код.   -  person Oleg Danu    schedule 22.02.2011
comment
Это теоретический вопрос. Я вижу две ситуации с NSString, и я просто хочу знать, в чем разница между stringWithFormat и stringWithString   -  person Bourne    schedule 22.02.2011


Ответы (2)


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

И поскольку жестко запрограммированный NSString никогда не освобождается, вы можете использовать его даже после того, как пул автозапуска освободит его.

Это просто покрывает ваше неправильное управление памятью. И рассчитывать на такое поведение точно не стоит.


Редактировать. больше нечего сказать. NSString, который вы создали с помощью stringWithString: literal, просто укажет на расположение литерала в памяти.

Небольшой пример:

NSString *string = @"Foo";
NSLog(@"%@ - %p", string, string);
NSString *string2 = string;
NSLog(@"%@ - %p", string2, string2);
NSString *string3 = [string copy];
NSLog(@"%@ - %p", string3, string3);
NSString *string4 = [string retain];
NSLog(@"%@ - %p", string4, string4);
NSString *string5 = [NSString stringWithString:string];
NSLog(@"%@ - %p", string5, string5);
NSString *string6 = [[NSString alloc] initWithString:string];
NSLog(@"%@ - %p", string6, string6);

вы увидите, что все они указывают на один и тот же адрес.

2011-02-22 13:24:41.202 xxx[40783:207] Foo - 0x74120
2011-02-22 13:24:41.204 xxx[40783:207] Foo - 0x74120
2011-02-22 13:24:41.204 xxx[40783:207] Foo - 0x74120
2011-02-22 13:24:41.206 xxx[40783:207] Foo - 0x74120
2011-02-22 13:24:41.206 xxx[40783:207] Foo - 0x74120
2011-02-22 13:24:41.207 xxx[40783:207] Foo - 0x74120

И все они никогда не будут освобождены. Таким образом, вы можете получить доступ к string5 из другого метода в вашем коде, и он все равно будет указывать на адрес 295740, а NSString «Foo» все еще будет там.

Но объект NSString, который не указывает на строковый литерал, будет освобожден в это время, и вы получите исключение плохого доступа.

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

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

person Matthias Bauch    schedule 22.02.2011
comment
пост слегка отредактировал. Я уже сделал то, что ответил Дэниел, и да, понял, что это работает. Не могли бы вы более подробно объяснить, почему stringWithFormat требует сохранения, а stringWithString работает без такой суеты? - person Bourne; 22.02.2011

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

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

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

person Daniel Tull    schedule 22.02.2011
comment
Отредактировал пост. Я понял это. После присвоения строке значения с помощью stringWithFormat я выполнил для нее сохранение, и никаких проблем не возникло. В то время как stringWithString не требует выполнения таких действий. Отсюда вопрос, как они обрабатываются внутри памяти. Спасибо за ответ. - person Bourne; 22.02.2011
comment
Насколько я везде читал, и stringWithString, и stringWithFormat возвращают автоматически выпущенные указатели. - person Bourne; 22.02.2011
comment
Это не связано с тем, что возвращается, а связано с тем, как работают внутренние компоненты компилятора. fluchtpunkt прекрасно показывает это в своей редакции. - person Daniel Tull; 22.02.2011