Слабая переменная NSString не равна нулю после установки единственной сильной ссылки на nil

У меня проблема с этим кодом:

__strong NSString *yourString = @"Your String"; __weak NSString *myString = yourString; yourString = nil; __unsafe_unretained NSString *theirString = myString; NSLog(@"%p %@", yourString, yourString); NSLog(@"%p %@", myString, myString); NSLog(@"%p %@", theirString, theirString); 

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

ТЛ; dr: Проблема в том, что строковый литерал никогда не освобождается, поэтому ваш слабый указатель все еще указывает на него.


теория

Сильные переменные сохраняют значение, на которое они указывают.

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

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


Литералы и константы

Когда вы создаете строку с использованием @"literal string" она становится строковым литералом, который никогда не изменится. Если вы используете одну и ту же строку во многих местах вашего приложения, это всегда один и тот же объект. Строковые литералы не исчезают. Использование [[NSString alloc] initWithString:@"literal string"] не изменит ситуацию. Поскольку он становится указателем на литеральную строку. Однако стоит отметить, что [[NSString alloc] initWithFormat:@"literal string"]; работает по-разному и освободит свой строковый объект.

Построчно:

 __strong NSString *yourString = @"Your String"; 

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

 __weak NSString *myString = yourString; 

Вы создаете слабый указатель на то же, что и ваш сильный указатель. Если в это время сильный указатель укажет на что-то еще, то значение, которое оно указывает, будет освобождено, тогда слабый указатель изменит его значение, чтобы он указывал на nil . Теперь он все же указывает на то же, что и сильный указатель.

 yourString = nil; 

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

 __unsafe_unretained NSString *theirString = myString; 

Создается новый недостижимый указатель, указывающий на ваш слабый указатель, который указывает на строковый литерал.

 NSLog(@"%p %@", yourString, yourString); NSLog(@"%p %@", myString, myString); NSLog(@"%p %@", theirString, theirString); 

Вы печатаете все свои строки и путаетесь, почему первое значение равно nil а два других – нет.


Связанные чтения:

В чем разница между строковой константой и строковым литералом?

Давид на 100% прав в своем ответе. Я просто добавил четыре явных примера, используя GHUnit .

Поведение classификатора жизненного цикла для ссылок на объекты.

Использование NSObject в качестве прокси для всех объектов, поведение квалификаторов жизненного цикла, как и ожидалось.

 - (void) test_usingNSObjects { NSObject *value1 = [[NSObject alloc] init]; NSObject *value2 = [[NSObject alloc] init]; NSObject *value3 = [[NSObject alloc] init]; __strong NSObject *sRefToValue = value1; __weak NSObject *wRefToValue = value2; __unsafe_unretained NSObject *uRefToValue = value3; value1 = value2 = value3 = nil; GHAssertNotNil(sRefToValue, @"Strong reference to the object that was originally \ assigned to value1. Even though value1 was set to nil, the \ strong reference to the object keeps the object from being \ destroyed."); GHAssertNil(wRefToValue, @"Weak reference to the object that was originally assigned to \ value2. When value2 was set to nil, the weak reference does \ not prevent the object from being destroyed. The weak \ reference is also set to nil."); // Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS // signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for // that code. #ifdef RECIEVE_EXC_BAD_ACCESS GHAssertNotNil(uRefToValue, @"Unsafe unretained reference to the object that was \ originally assigned to value3. When value3 was set to nil, \ the unsafe unretained reference does not prevent the object \ from being destroyed. The unsafe unretained reference is \ unaltered and the reference is invalid. Accessing the \ reference will result in EXC_BAD_ACCESS signal."); #endif // To avoid future EXC_BAD_ACCESS signals. uRefToValue = nil; } . - (void) test_usingNSObjects { NSObject *value1 = [[NSObject alloc] init]; NSObject *value2 = [[NSObject alloc] init]; NSObject *value3 = [[NSObject alloc] init]; __strong NSObject *sRefToValue = value1; __weak NSObject *wRefToValue = value2; __unsafe_unretained NSObject *uRefToValue = value3; value1 = value2 = value3 = nil; GHAssertNotNil(sRefToValue, @"Strong reference to the object that was originally \ assigned to value1. Even though value1 was set to nil, the \ strong reference to the object keeps the object from being \ destroyed."); GHAssertNil(wRefToValue, @"Weak reference to the object that was originally assigned to \ value2. When value2 was set to nil, the weak reference does \ not prevent the object from being destroyed. The weak \ reference is also set to nil."); // Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS // signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for // that code. #ifdef RECIEVE_EXC_BAD_ACCESS GHAssertNotNil(uRefToValue, @"Unsafe unretained reference to the object that was \ originally assigned to value3. When value3 was set to nil, \ the unsafe unretained reference does not prevent the object \ from being destroyed. The unsafe unretained reference is \ unaltered and the reference is invalid. Accessing the \ reference will result in EXC_BAD_ACCESS signal."); #endif // To avoid future EXC_BAD_ACCESS signals. uRefToValue = nil; } 

Действие classификатора жизненного цикла для буквального NSString s (@ “something”).

Это в основном то же самое, что и test_usingNSObjects , но вместо использования NSObject используется NSString которому назначена буквальная строка. Поскольку литеральные строки не уничтожаются, как другие объекты, __weak различные поведения для переменных __weak и __unsafe_unretained .

 - (void) test_usingLiteralNSStrings { NSString *value1 = @"string 1"; NSString *value2 = @"string 2"; NSString *value3 = @"string 3"; __strong NSString *sRefToValue = value1; __weak NSString *wRefToValue = value2; __unsafe_unretained NSString *uRefToValue = value3; value1 = value2 = value3 = nil; GHAssertNotNil(sRefToValue, @"Strong reference to the object that was originally \ assigned to value1. Even though value1 was set to nil, \ literal strings are not destroyed."); GHAssertNotNil(wRefToValue, @"Weak reference to the object that was originally assigned \ to value2. Even though value2 was set to nil, \ literal strings are not destroyed so the weak reference is \ still valid."); GHAssertNotNil(uRefToValue, @"Unsafe unretained reference to the object that was \ originally assigned to value3. Even though value3 was set \ to nil, literal strings are not destroyed so the unsafe \ unretained reference is still valid."); } 

Поведение classификатора жизненного цикла для нелитературных NSString s.

Это в основном то же самое, что и test_usingNSObjects , но вместо использования NSObject используется NSString которому назначена нелитеративная строка. Поскольку нелитературные строки уничтожаются, как и другие объекты, поведение такое же, как и в test_usingNSObjects .

 - (void) test_usingNonliteralNSStrings { NSString *value1 = [[NSString alloc] initWithFormat:@"string 1"]; NSString *value2 = [[NSString alloc] initWithFormat:@"string 2"]; NSString *value3 = [[NSString alloc] initWithFormat:@"string 3"]; __strong NSString *sRefToValue = value1; __weak NSString *wRefToValue = value2; __unsafe_unretained NSString *uRefToValue = value3; value1 = value2 = value3 = nil; GHAssertNotNil(sRefToValue, @"Strong reference to the object that was originally \ assigned to value1. Even though value1 was set to nil, the \ strong reference to the object keeps the object from being \ destroyed."); GHAssertNil(wRefToValue, @"Weak reference to the object that was originally assigned to \ value2. When value2 was set to nil, the weak reference does \ not prevent the object from being destroyed. The weak \ reference is also set to nil."); // Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS // signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for // that code. #ifdef RECIEVE_EXC_BAD_ACCESS GHAssertNotNil(uRefToValue, @"Unsafe unretained reference to the object that was \ originally assigned to value3. When value3 was set to nil, \ the unsafe unretained reference does not prevent the object \ from being destroyed. The unsafe unretained reference is \ unaltered and the reference is invalid. Accessing the \ reference will result in EXC_BAD_ACCESS signal."); #endif // To avoid future EXC_BAD_ACCESS signals. uRefToValue = nil; } . - (void) test_usingNonliteralNSStrings { NSString *value1 = [[NSString alloc] initWithFormat:@"string 1"]; NSString *value2 = [[NSString alloc] initWithFormat:@"string 2"]; NSString *value3 = [[NSString alloc] initWithFormat:@"string 3"]; __strong NSString *sRefToValue = value1; __weak NSString *wRefToValue = value2; __unsafe_unretained NSString *uRefToValue = value3; value1 = value2 = value3 = nil; GHAssertNotNil(sRefToValue, @"Strong reference to the object that was originally \ assigned to value1. Even though value1 was set to nil, the \ strong reference to the object keeps the object from being \ destroyed."); GHAssertNil(wRefToValue, @"Weak reference to the object that was originally assigned to \ value2. When value2 was set to nil, the weak reference does \ not prevent the object from being destroyed. The weak \ reference is also set to nil."); // Removing the #ifdef and #endif lines will result in a EXC_BAD_ACCESS // signal. Receiving a EXC_BAD_ACCESS signal is the expected behavior for // that code. #ifdef RECIEVE_EXC_BAD_ACCESS GHAssertNotNil(uRefToValue, @"Unsafe unretained reference to the object that was \ originally assigned to value3. When value3 was set to nil, \ the unsafe unretained reference does not prevent the object \ from being destroyed. The unsafe unretained reference is \ unaltered and the reference is invalid. Accessing the \ reference will result in EXC_BAD_ACCESS signal."); #endif // To avoid future EXC_BAD_ACCESS signals. uRefToValue = nil; } 

Создание NSString – буквальное или нелитеративное.

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

 - (void) test_stringCreation { NSString *literalString = @"literalString"; NSString *referenced = literalString; NSString *copy = [literalString copy]; NSString *initWithString = [[NSString alloc] initWithString:literalString]; NSString *initWithFormat = [[NSString alloc] initWithFormat:@"%@", literalString]; // Testing that the memory addresses of referenced objects are the same. GHAssertEquals(literalString, @"literalString", @"literal"); GHAssertEquals(referenced, @"literalString", @"literal"); GHAssertEquals(copy, @"literalString", @"literal"); GHAssertEquals(initWithString, @"literalString", @"literal"); GHAssertNotEquals(initWithFormat, @"literalString", @"nonliteral - referenced objects' memory addresses are \ different."); // Testing that the objects referenced are equal, ie isEqual: . GHAssertEqualObjects(literalString, @"literalString", nil); GHAssertEqualObjects(referenced, @"literalString", nil); GHAssertEqualObjects(copy, @"literalString", nil); GHAssertEqualObjects(initWithString, @"literalString", nil); GHAssertEqualObjects(initWithFormat, @"literalString", nil); // Testing that the strings referenced are the same, ie isEqualToString: . GHAssertEqualStrings(literalString, @"literalString", nil); GHAssertEqualStrings(referenced, @"literalString", nil); GHAssertEqualStrings(copy, @"literalString", nil); GHAssertEqualStrings(initWithString, @"literalString", nil); GHAssertEqualStrings(initWithFormat, @"literalString", nil); } 

слабое свойство будет установлено равным нулю после того, как пул авторезистов будет слит.

пытаться:

 @autoreleasepool { _strong NSString *yourString = @"Your String"; __weak NSString *myString = yourString; yourString = nil; __unsafe_unretained NSString *theirString = myString; } NSLog(@"%p %@", yourString, yourString); NSLog(@"%p %@", myString, myString); NSLog(@"%p %@", theirString, theirString); 
  • Объяснение сильного и слабого хранения в iOS5
  • Различия между зависимостью управления и зависимостями в Maven
  • Swift: защита против, если пусть
  • Прочтите CSV из github в R
  • Почему @autoreleasepool все еще нуждается в ARC?
  • Как я могу назвать оригинальный «оператор new», если я его перегрузил?
  • Как тег отличается от ветки в Git? Что я должен использовать здесь?
  • Найти физический адрес таблицы векторов исключений из модуля ядра
  • @ «Какой-то текст» дает автореализацию или сохранение 1 объекта назад?
  • Как я могу найти элементы управления WPF по имени или типу?
  • Лучшая практика для определения событий кнопки в android
  • Давайте будем гением компьютера.