Получи мою голову вокруг неизменности

13

Я новичок в объектно-ориентированном программировании, и одна концепция, на которую у меня ушло некоторое время, - это неизменность. Я думаю, что лампочка погасла прошлой ночью, но я хочу проверить:

Когда я сталкиваюсь с утверждениями, что неизменяемый объект не может быть изменен, я озадачен, потому что я могу, например, сделать следующее:

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

Там я только что изменил myName неизменяемого типа NSString. Моя проблема в том, что слово «объект» может относиться к физическому объекту в памяти или абстракции «myName». Первое определение относится к понятию неизменности.

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

Это правильно, или я все еще заблудился в лесу?

Майкл Мангольд
источник
13
Ваш тип не NSStringявляется " указателем на" и NSStringне является неизменным. Я ничего не знаю об объективном C, но я предполагаю , что в вашем примере , что @"Mike"создает новый экземпляр NSStringи присваивает его указатель , myName. Таким образом, вы не изменили объект, на который myNameуказывал, только то, на что он указывал.
fwgx
2
@fwgx Поместите это как ответ, и вы получите мое повышение.
Гюльшан
Спасибо всем за все ответы, они очень полезны. Теперь я понимаю, что неизменный объект - это значение в памяти, а не переменная, которая на него указывает.
Майкл Мангольд
@Gulshan Готово, см. Ниже ....
fwgx
Чтобы понять неизменность, я рекомендую изучать язык программирования, который четко разделяет привязку (то есть, присваивает имена объектам) с изменчивостью (т.е. позволяет переназначать имя другому объекту). ML (в любом виде: SML, Ocaml, F #) является хорошим примером.
Жиль "ТАК - перестань быть злым"

Ответы:

4

Похоже, вы движетесь в правильном направлении, но пока не совсем поняли. Это не правильно:

Там я только что изменил myName неизменяемого типа NSString. Моя проблема в том, что слово «объект» может относиться к физическому объекту в памяти или абстракции «myName».

В фрагменте кода, myNameне неизменного типа NSString, он имеет изменяемый тип NSString*(указатель на NSString). Похоже, что ключевая вещь, которую вы упускаете, - это понять, что указатель - это просто другое значение , и он имеет полностью отдельную жизнь от того, на что он указывает (или вещей, если вы меняете его частично в течение всей своей жизни).

Ты говоришь:

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

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

Итак, два NSStringобъекта в вашем примере ( @"Bob"и @"Mike") полностью отделены от myNameпеременной. Они также полностью отделены друг от друга. Когда вы изменяете, myNameчтобы указать @"Mike"вместо того, чтобы указывать @"Bob", вы не меняете NSStringобъекты.


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

Джон Варфоломей
источник
17

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

Контрпример в C (немного упрощенный, предполагая архитектуру, которая позволяет это):

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

Теперь больше не «содержит» (то есть указывает на) строку «Hello World», но вместо этого это «Yello World».

В языках, где строки являются неизменяемыми, таких как Java и (EDIT: safe) C #, вы не можете сделать это. Ни за что. Это означает, что каждая часть программы может безопасно хранить ссылку на строку и полагать, что ее содержимое никогда не изменится; в противном случае им пришлось бы создать копию, чтобы быть в безопасности.

Но переменная все еще изменчива. Вы можете позволить ему указывать на другой объект. Просто сам объект не изменится за вашей спиной.

user281377
источник
1
Технически вы можете изменять содержимое строки в C #, вам просто нужно использовать unsafeкод.
Aaronaught
1
Aaronaught: Спасибо за информацию, вы правы; для всех, кому нужно знать, как: msdn.microsoft.com/en-us/library/ms228599.aspx
user281377
10

Вы путаете переменные с объектами. Переменные могут использоваться для хранения ссылок на объекты, но они НЕ являются объектами. Это неизменяемые объекты, а не переменные, поэтому вы можете изменять переменную от одного объекта к другому, но вы не можете изменить атрибуты объекта, если он неизменен.

Думайте об объекте как о громком пьяном соседе. Если он разумный (изменчивый), вы можете постучать в его дверь и превратить его в образ жизни, в котором он не делает столько шума. Но если он неизменен, твое единственное изменение - надеяться, что кто-то еще придет!

Килиан Фот
источник
3
У меня были соседи, которых я бы хотел отключить.
Дэйв Най
Я не думаю, что вам нужен ваш пример во втором абзаце, поскольку ваше объяснение достаточно хорошее. ИМО пример может просто запутать. Однако +1 за краткий ответ на вопрос главы.
Пол МакКейб
@DaveNay: я прошу прощения за стелс каламбур. Не было предназначено вообще.
Килиан Фот
6

Переменная не является объектом. Переменная - это имя, которое относится к объекту (или, в более общем случае, к значению).

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

Оператор присваивания - это то, что делает переменные изменяемыми (некоторые языки, такие как Haskell, не имеют его и фактически используют неизменяемые переменные). Это позволяет вам переопределить значение имени и тем самым переназначить значение.

Теперь сами объекты могут быть неизменными. Несколько тысяч лет назад можно было подумать, что бриллианты неизменны. Неважно, что вы сделали с бриллиантом, вы не можете изменить его. Назовите ли вы его waggawooga (в широком смысле означает «самый большой блестящий камень нашего племени») или перестали называть его так (потому что вы нашли больший), бриллиант остался прежним. В отличие от этого, кусок дерева, который вы вырезали на забавных картинках с помощью вашего waggawooga, не остался прежним. Это оказалось изменчивым. Даже если у него было одно и то же имя все время.

И переменные, и значения могут быть неизменными (независимо). В данном случае это неизменные объекты. После того как вы построите NSString, вы не можете изменить его. Вы можете называть это именами и передавать их, но это останется прежним. В отличие от этого, NSMutableStringможет быть изменено после создания, например, путем вызова setStringметода.

back2dos
источник
Люблю сравнение Waggawooga!
Майкл К
Просто, чтобы не путать исходный плакат больше, чем необходимо: хотя ваша точка зрения, что переменная не является объектом, является хорошей, переменная фактически не определяется как «имя, которое ссылается на значение». Возможно , переменная имеет имя и относится к хранилищу, которое содержит значение. Во многих языках программирования переменные не нужно называть, и во многих языках одна и та же переменная может иметь более одного имени.
Эрик Липперт
4

Я думаю, что вы чувствуете себя потерянным, потому что вы смешиваете два понятия: сам объект и имя переменной, связанной с этим объектом.

Неизменяемые объекты не могут быть изменены. Период. Однако имя переменной (символ), привязанное к неизменяемому объекту, может быть изменено для привязки к другому неизменяемому объекту.

Другими словами, то, что вы сделали в этих двух строках:

  1. создать неизменяемый строковый объект со значением "Bob"
  2. привязать символ myNameк этому объекту
  3. создать неизменный строковый объект со значением "Mike"
  4. привязать символ myNameк этому объекту
Vartec
источник
2

Ваш ваш тип не является NSString, это « указатель наNSString », который не является неизменным. Я ничего не знаю о цели C, но я предполагаю, что в вашем примере @"Mike"создается новый экземпляр объекта NSStringи назначается его указателю myName . Таким образом , вы не изменили объект , который myNameбыл указывая на, как раз то , что она показывала.

fwgx
источник
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";

вы не добавляете "World" в конец экземпляра "Hello"; Вы создаете новый экземпляр , копируете в него «Hello World» и обновляете fooего, ссылаясь на этот новый экземпляр строки.

fooне содержит сам экземпляр строки; он относится только к этому экземпляру (аналогично указателю в C или Obj-C), поэтому типы типа String называются ссылочными типами.

Джон Боде
источник