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


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

как" язык программирования 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) 

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

5 83

5 ответов:

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

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

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

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

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

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

обновление

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

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

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

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

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

Я надеюсь, что это помогает.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

рассмотрим Свифта Array структура (да это структуры).

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

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

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

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