Подтвердить что ты не робот

Быстрая и мутирующая структура

Есть что-то, что я не совсем понимаю, когда речь идет о мутировании типов значений в Swift.

Как "Быстрый язык программирования" iBook утверждает: По умолчанию свойства типа значения не могут быть изменены из его методов экземпляра.

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

То, что не совсем ясно для меня, таково: Вы можете изменить var извне структуры, но вы не можете изменить ее из своих собственных методов. Это кажется мне интригующим, как и в объектно-ориентированных языках, вы обычно пытаетесь инкапсулировать переменные, чтобы их можно было изменить только изнутри. С структурами это выглядит наоборот. Чтобы разработать здесь фрагмент кода:

struct Point {
    var x = 0, y = 0
    mutating func moveToX(x: Int, andY y:Int) { //Needs to be a mutating method in order to work
        self.x = x
        self.y = y
    }
}

var p = Point(x: 1, y: 2)
p.x = 3 //Works from outside the struct!
p.moveToX(5, andY: 5) 

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

4b9b3361

Ответ 1

Атрибут mutability помечен на хранилище (константа или переменная), а не тип. Вы можете думать, что структура имеет два режима: изменчивый и неизменный. Если вы присваиваете значение struct неизменяемому хранилищу (мы называем его let или константой в Swift), значение становится неизменным, и вы не можете изменить какое-либо состояние в значении. (включая вызов любого метода мутации)

Если значение присваивается изменяемому хранилищу (мы называем его var или переменной в Swift), вы можете изменять их состояние и разрешать метод mutating.

Кроме того, классы не имеют этого неизменяемого/изменяемого режима. ИМО, это связано с тем, что классы обычно используются для представления ссылочного объекта. И ссылочный объект обычно изменен, потому что очень сложно создавать и управлять ссылочными графами объектов неизменным образом с надлежащей производительностью. Они могут добавить эту функцию позже, но не сейчас.

Для программистов Objective-C очень знакомы изменяемые/неизменные понятия. В Objective-C у нас было два разделенных класса для каждой концепции, но в Swift вы можете сделать это с помощью одной структуры. Половина работы.

Для программистов на C/С++ это тоже очень знакомая концепция. Это именно то, что ключевое слово const делает в C/С++.

Кроме того, неизменяемое значение может быть очень хорошо оптимизировано. Теоретически компилятор Swift (или LLVM) может выполнять копирование по значениям, переданным let, точно так же, как С++. Если вы используете неизменяемую структуру с умом, она будет превосходить обновленные классы.

Update

Как сказал @Joseph, это не дает объяснения, почему я добавляю немного больше.

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

Тогда непреложные методы должны гарантировать эту семантическую неизменность. Это означает, что он не должен изменять какое-либо внутреннее значение. Поэтому компилятор запрещает любые изменения состояния самого себя в неизменяемом методе. Напротив, мутирующие методы могут свободно изменять состояния.

И тогда у вас может возникнуть вопрос, почему неизменяемым является значение по умолчанию? Это потому, что очень трудно предсказать будущее состояние мутирующей ценности, и это обычно становится основным источником головных болей и ошибок. Многие люди согласились с тем, что решение избегает изменчивых материалов, а затем неизменяемо по умолчанию в течение десятилетий находилось на вершине списка пожеланий на языках семейства C/С++ и его выводах.

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

Надеюсь, это поможет.

Ответ 2

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

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

Чтобы достичь этого, Swift делит структурные методы на две категории: те, которые изменяют базовую структуру, и поэтому могут быть вызваны только в экземплярах изменчивой структуры, а также те, которые не изменяют базовую структуру и поэтому должны быть invokable на обоих изменяемых и неизменные случаи. Последнее использование, вероятно, является более частым и, следовательно, является стандартным.

Для сравнения,.NET в настоящее время (по-прежнему!) не предлагает средств для различения структурных методов, которые изменяют структуру из тех, которые этого не делают. Вместо этого вызов метода структуры в экземпляре неизменяемой структуры заставит компилятор сделать изменяемую копию экземпляра структуры, позволить методу делать все, что ему нужно, и отбросить копию, когда метод будет выполнен. Это приводит к тому, что компилятор вынужден тратить время на копирование структуры независимо от того, модифицирует ли этот метод, даже если добавление операции копирования почти никогда не превратит семантически неправильный код в семантико-правильный код; это просто приведет к тому, что код семантически ошибочен одним способом (изменением "неизменяемого" значения), чтобы быть неправильным по-другому (позволяя коду думать, что он модифицирует структуру, но отбрасывает предпринятые изменения). Разрешение методам struct указывать, будут ли они изменять базовую структуру, может исключить необходимость в бесполезной операции копирования, а также гарантирует, что попытка ошибочного использования будет отмечена.

Ответ 3

Предостережение: условия непрофессионалов впереди.

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

Поэтому я хочу попытаться просто и прямо ответить на вопрос "почему".

Чтобы быть точным: почему мы должны отмечать функции struct как mutating, когда мы можем изменять параметры структуры без каких-либо изменяющих ключевых слов?

Итак, большая картина, она имеет много общего с философией, которая быстро поддерживает Swift.

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

В этой ситуации Swift похож на почтовое отделение. Если много людей с большим количеством контактов передвигаются много, у него действительно высокие накладные расходы. Он должен заплатить большой штат людей за обработку всех этих уведомлений, и процесс требует много времени и усилий. Именно поэтому идеальное состояние Swift для каждого в своем городе имеет как можно меньше контактов. Тогда ему не нужен большой штат для обработки изменений адреса, и он может делать все остальное быстрее и лучше.

Это также объясняет, почему Swift-люди все бредят по типам значений по сравнению с ссылочными типами. По своей природе ссылочные типы набирают "контакты" повсюду, а типам значений обычно не требуется больше пары. Типы значений - "Swift" -er.

Итак, вернемся к маленькому изображению: structs. Структуры очень важны в Swift, потому что они могут делать большую часть объектов, которые могут делать объекты, но они являются типами значений.

Продолжим аналогию с физическим адресом, представив misterStruct, который живет в someObjectVille. Аналогия здесь немного подкупается, но я думаю, что это все же полезно.

Чтобы смоделировать изменение переменной на struct, скажем, misterStruct имеет зеленые волосы и получает заказ переключиться на синие волосы. Аналогия получает, как я уже сказал, как бы то ни было, но что происходит, так это то, что вместо того, чтобы менять волосы misterStruct, старый человек уходит, и новый человек с синими волосами перемещается, и этот новый человек начинает называть себя misterStruct. Никто не должен получать уведомление об изменении адреса, но если кто-то посмотрит на этот адрес, они увидят парня с синими волосами.

Теперь давайте моделировать то, что происходит, когда вы вызываете функцию на struct. В этом случае он как misterStruct получает такой порядок, как changeYourHairBlue(). Поэтому почтовое отделение доставляет инструкцию misterStruct "перемените волосы на синие и скажите мне, когда закончите".

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

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

[Чтобы действительно выиграть эту аналогию, что-то ужасное, то, что технически случилось с зеленым парнем, было то, что после того, как он ушел, он сразу же покончил жизнь самоубийством. Поэтому он не может никому никому сообщить, что задача выполнена!]

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

И поэтому Swift хочет, чтобы мы использовали ключевое слово mutating!

Конечный результат выглядит так же, как и все, что относится к структуре: у жителя дома теперь синие волосы. Но процессы его достижения на самом деле совершенно разные. Похоже, он делает то же самое, но это совсем другое дело. Это делает то, что структуры Swift вообще никогда не делают.

Итак, чтобы дать плохому компилятору небольшую помощь, и не заставляйте его выяснять, не изменяет ли функция struct или нет сама по себе для каждой функции struct когда-либо, нас просят иметь жалость и используйте ключевое слово mutating.

В сущности, чтобы помочь Быстрому пребыванию быстро, мы все должны сделать свою часть.:)

EDIT:

Привет, чувак /dudette, который меня подавил, я просто полностью переписал свой ответ. Если он сидит лучше с вами, вы удалите нижний план?

Ответ 4

Структуры Swift могут быть созданы как константы (через let) или переменные (через var)

Рассмотрим Swift Array struct (да это структура).

var petNames: [String] = ["Ruff", "Garfield", "Nemo"]
petNames.append("Harvey") // ["Ruff", "Garfield", "Nemo", "Harvey"]

let planetNames: [String] = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
planetNames.append("Pluto") //Error, sorry Pluto. No can do

Почему приложение не присоединилось к планете? Поскольку append помечен ключевым словом mutating. И так как planetNames был объявлен с использованием let, все отмеченные таким образом методы являются пределами.

В вашем примере компилятор может сказать, что вы изменяете структуру, назначая одному или нескольким своим свойствам вне init. Если вы немного измените код, вы увидите, что x и y не всегда доступны вне структуры. Обратите внимание на let в первой строке.

let p = Point(x: 1, y: 2)
p.x = 3 //error
p.moveToX(5, andY: 5) //error

Ответ 5

Рассмотрим аналогию с С++. Структурный метод в Swift, являющийся mutating/not- mutating, аналогичен методу на С++, который не является const/const. Метод, отмеченный const в С++ аналогичным образом, не может мутировать структуру.

Вы можете изменить var извне структуры, но вы не можете ее изменить из собственных методов.

В С++ вы также можете "изменить var извне структуры", но только если у вас есть структурная переменная не const. Если у вас есть структурная переменная const, вы не можете назначить var, и вы также не можете вызвать метод const. Аналогично, в Swift вы можете изменить свойство структуры только в том случае, если структурная переменная не является константой. Если у вас есть константа struct, вы не можете назначить свойство, и вы также не можете вызвать метод mutating.

Ответ 6

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

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

Напротив, установка свойства вне вашей структуры работает на известном экземпляре этой структуры. Если он назначен константе, Xcode сообщит вам об этом в тот момент, когда вы ввели вызов метода.

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