Обіймати голову навколо непорушності


13

Я новачок в об’єктно-орієнтованому програмуванні, і одна концепція, яка займає мене час, - це незмінність. Я думаю, що лампочка згасла минулої ночі, але я хочу перевірити:

Коли я стикаюся з твердженнями, що незмінний об'єкт неможливо змінити, я здивований, бо можу, наприклад, зробити наступне:

NSString *myName = @"Bob";
myName = @"Mike";

Там я щойно змінив своє ім'я, непорушного типу NSString. Моя проблема полягає в тому, що слово "об'єкт" може позначати фізичний об'єкт в пам'яті або абстракцію "myName". Перше визначення стосується поняття незмінності.

Що стосується змінної, то більш чітким (для мене) визначенням незмінності є те, що значення незмінного об'єкта можна змінити лише шляхом зміни його місця в пам'яті, тобто його посилання (також відомого як його покажчик).

Це правильно, чи я все-таки загубився в лісі?


13
Ваш тип не є NSString, це " вказівник на та NSString", який не змінюється. Я нічого не знаю про об'єктивне C, але я припускаю , що в вашому прикладі , що @"Mike"створює новий екземпляр NSStringі привласнює його покажчик , myName. Отже, ви не змінили об'єкт, на який myNameвказували, а лише на те, на що він вказував.
fwgx

2
@fwgx Поставте це як відповідь, і ви отримаєте мою нагороду.
Гульшан

Дякую всім за всі відповіді, вони дуже корисні. Тепер я розумію, що незмінним об'єктом є значення в пам'яті, а не змінна, яка вказує на нього.
Майкл Мангольд

@Gulshan Готово, див. Нижче ....
fwgx

Щоб зрозуміти незмінність, я рекомендую вивчити мову програмування, яка чітко відокремлює прив'язку (тобто надання імен об'єктам) з можливістю зміни (тобто дозволяє перейменувати ім'я на інший об'єкт). ML (у будь-якому вигляді: SML, Ocaml, F #) - хороший приклад.
Жил 'ТАК - перестань бути злим'

Відповіді:


4

Здається, ви рухаєтесь у правильному напрямку, але ще не зовсім зрозуміли. Це неправильно:

Там я щойно змінив своє ім'я, непорушного типу NSString. Моя проблема полягає в тому, що слово "об'єкт" може позначати фізичний об'єкт в пам'яті або абстракцію "myName".

У вашому фрагменті коду myNameне є незмінний тип NSString, він має змінний тип NSString*(вказівник на NSString). Здається, що ключовим, що вам не вистачає, є розуміння того, що вказівник - це лише інше значення , і воно має абсолютно окреме життя від речі, на яку він вказує (або речі, якщо змінити її частково протягом життя).

Ти кажеш:

... значення незмінного об'єкта може бути змінено лише шляхом зміни його місця в пам'яті, тобто його посилання (також відомого як його вказівник).

Це неправильно. Об'єкт не володіє вказівниками, які вказують на нього, а також місце управління пам'яттю об'єкта не контролюється або іншим чином не впливає жодними вказівниками на нього.

Отже, два NSStringоб'єкти у вашому прикладі ( @"Bob"та @"Mike") повністю відокремлені від myNameзмінної. Вони також повністю відокремлені один від одного. Коли ви змінюєтесь myNameна пункт, @"Mike"а не на вказівник @"Bob", ви не змінюєте NSStringоб'єктів.


Для повноти зазначу, що збирачі сміття роблять це більш складним, оскільки зміни вказівників можуть впливати на об’єкти, на які вони вказують (ред.). Однак це деталь реалізації, який не повинен впливати на поведінку коду, що спостерігається.


17

Ви втрачаєте слова. Незмінність означає: Поки ви не зміните змінну, вона завжди буде "містити" те саме значення, незалежно від того, що ви робите з іншими змінними.

Контрприклад в C (трохи спрощений, якщо припустити архітектуру, яка це дозволяє):

 char *a = "Hello World";
 char *b = a;
 b[0] = 'Y';

Тепер більше не "містить" (тобто вказує на) рядок "Hello World", але це "Yello World".

У мовах, де рядки незмінні, як-от Java та (EDIT: safe) C #, ви не можете цього зробити. У жодному разі. Це означає, що кожна частина програми може безпечно зберігати посилання на рядок і покладатися на те, що її вміст ніколи не змінюється; інакше їм доведеться створити копію лише для того, щоб бути захищеною.

Але змінна все ще змінюється. Ви можете дозволити йому вказувати на інший об’єкт. Просто об’єкт сам не зміниться за вашою спиною.


1
Ви технічно можете змінювати вміст рядків у C #, просто потрібно використовувати unsafeкод.
Aaronaught

1
Aaronaught: Дякую за інформацію, ви маєте рацію; для всіх, хто повинен знати як: msdn.microsoft.com/en-us/library/ms228599.aspx
користувач281377

10

Ви плутаєте змінні з об'єктами. Змінні можна використовувати для зберігання посилань на об'єкти, але вони НЕ є об’єктами. Це незмінні об'єкти, а не змінні, тому ви можете змінювати змінну з одного об'єкта на інший, але ви не можете змінити атрибути об'єкта, якщо він незмінний.

Подумайте про об’єкт як про голосного, п’яного сусіда. Якщо він розумний (незмінний), ви, можливо, зможете постукати у його двері та перетворити його на спосіб життя, де він не видає стільки шуму. Але якщо він непорушний, ваша єдина зміна - сподіватися, що хтось інший переїде!


3
У мене були сусіди, яких я б хотів відключити.
Дейв Най

Я не думаю, що вам потрібен ваш приклад у другому абзаці, оскільки ваше пояснення досить добре. Приклад IMO може просто заплутати. Однак +1 для стислої відповіді на питання хлопця.
Пол МакКейб

@DaveNay: Прошу вибачення за скритий каламбур. Зовсім не було призначено.
Кіліан Фот

6

Змінна не є об'єктом. Змінна - це ім'я, яке посилається на об'єкт (або загалом - значення).

Наприклад, "воротар" - це ім'я, яке ми використовуємо для позначення об'єкта (особи), відповідального за захист мети. Але якщо я заміню цю людину іншою (бо колишній травмований чи що-небудь), нову людину зараз називають "воротарем".

Заява про присвоєння робить те, що змінні змінюються (деякі мови, такі як Haskell, не мають, і фактично використовують незмінні змінні). Це дозволяє перевизначити значення імені і тим самим переназначити значення.

Тепер самі предмети можуть бути незмінні. Кілька тисяч років тому можна було б подумати про алмази як про непорушні. Незалежно від того, що ви робили з діамантом, ви не могли його змінити. Незалежно від того, ви називали його waggawooga (вільно перекладається на "найбільший блискучий камінь нашого племені") чи перестали називати його таким чином (тому що ви знайшли більшого), алмаз залишився колишнім. На відміну від того дерева, який ви вирізали у кумедних картинках зі своєю ваггавугою, не залишився колишнім. Це виявилося незмінним. Навіть якби він весь час мав одне ім’я.

І змінні, і значення можуть бути незмінними (незалежно). У цьому випадку це незмінні предмети. Після того як ви сконструюєте антену NSString, ви не можете її змінити. Ви можете назвати його імена та передавати його навколо, але воно залишиться таким же. На відміну від цього, NSMutableStringможна змінити після створення, наприклад, викликавши setStringметод.


Любіть порівняння waggawooga!
Майкл К

Так само, щоб не плутати оригінальний плакат більше, ніж потрібно: хоча ваша думка, що змінна не є об’єктом, є доброю, змінна насправді не визначається як "ім'я, яке посилається на значення". Змінна, можливо, названа і відноситься до місця зберігання, яке містить значення. У багатьох мовах програмування змінні не потрібно називати, а в багатьох мовах одна і та ж змінна може мати більше одного імені.
Ерік Ліпперт

4

Я думаю, що ти відчуваєш себе втраченим, тому що ти змішуєш два поняття: сам об’єкт та ім’я змінної, прив'язаної до цього об’єкта.

Змінювані об’єкти неможливо змінити. Період. Однак ім'я змінної (символ), пов'язане з незмінним об'єктом, може змінюватися таким чином, щоб бути пов'язаним з іншим незмінним об'єктом.

Іншими словами, те, що ви зробили в цих двох рядках:

  1. створити незмінний рядковий об'єкт зі значенням "Bob"
  2. прив’язувати символ myNameдо цього об'єкта
  3. створити незмінний рядковий об'єкт зі значенням "Майк"
  4. прив’язувати символ myNameдо цього об'єкта

2

Ваш ваш тип не є NSString, це « покажчик наNSString ", який не є незмінним. Я не знаю нічого об'єктивного C, але я думаю, що у вашому прикладі @"Mike"є створення нового екземпляра NSStringта присвоєння його покажчику myName . Таким чином , ви не змінили об'єкт , який myNameбув вказуючи на, як раз те , що вона показувала.


0

Ось приклад змінного об’єкта: масив charв C:

char str[10];

Я можу змінити вміст з strдо змісту мого серця (так довго , як це не більше 10 символів). Щоб його можна було розпізнати як рядок за допомогою функцій бібліотеки струн C, має бути кінцевий 0, щоб він міг містити рядки довжиною до 9 символів:

strcpy(str, "hello"); // str now contains the string "hello\0"
strcpy(str, "world"); // str now contains the string "world\0"

str[0] = 'W';         // str now contains "World\0"

унд так сильніше .

Порівняйте це зі струнним об'єктом на Java:

String foo = "Hello";

Екземпляр рядка (фрагмент пам'яті, який містить символи 'H', 'e', ​​'l', 'l' і 'o') не може бути змінений; Я не можу змінити будь-який вміст цього рядка. Коли пишеш щось на кшталт

foo = foo + " World";

ви не додаєте "Світ" до кінця "Привіт"; ви створюєте новий екземпляр , копіюючи на нього "Hello World" та оновлюючи, fooщоб посилатися на цей новий екземпляр рядка.

fooсам по собі не містить екземпляр рядка; він посилається лише на цей екземпляр (подібний до вказівника на C або Obj-C), отже чому такі типи, як String, називають еталонними типами.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.