Programing

신속하고 변형 된 구조체

lottogame 2020. 9. 14. 21:34
반응형

신속하고 변형 된 구조체


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) 

구조가 자신의 컨텍스트 내에서 내용을 변경할 수없는 반면 내용은 다른 곳에서 쉽게 변경할 수있는 이유를 아는 사람이 있습니까?


가변성 속성은 유형이 아닌 저장소 (상수 또는 변수)에 표시됩니다. struct에는 mutableimmutable의 두 가지 모드가 있다고 생각할 수 있습니다 . 당신은 불변의 저장 (우리가 전화로 구조체의 값을 할당하는 경우 let또는 일정을 스위프트에서) 값이 불변 모드가되고, 당신은 가치의 상태를 변경할 수 없습니다. (변이 메서드 호출 포함)

값이 변경 가능한 저장소 ( Swift에서는 변수var 또는 변수 라고 부름)에 할당 된 경우 자유롭게 상태를 수정할 수 있으며 mutating 메서드 호출이 허용됩니다.

또한 클래스에는이 변경 불가능 / 변경 가능 모드가 없습니다. IMO는 클래스가 일반적으로 참조 가능한 엔티티 를 나타내는 데 사용되기 때문 입니다. 그리고 참조 가능한 엔티티는 적절한 성능으로 불변 방식으로 엔티티의 참조 그래프를 만들고 관리하기가 매우 어렵 기 때문에 일반적으로 변경 가능합니다. 나중에이 기능을 추가 할 수 있지만 적어도 지금은 아닙니다.

Objective-C 프로그래머에게는 변경 가능 / 불변 개념이 매우 익숙합니다. Objective-C에서는 각 개념에 대해 두 개의 분리 된 클래스가 있었지만 Swift에서는 하나의 구조체로이를 수행 할 수 있습니다. 절반은 일.

C / C ++ 프로그래머에게도 이것은 매우 친숙한 개념입니다. 이것이 바로 constC / C ++에서 키워드 가하는 일 입니다.

또한 불변의 값은 매우 멋지게 최적화 될 수 있습니다. 이론적으로 Swift 컴파일러 (또는 LLVM)는 letC ++처럼 전달 된 값에 대해 복사 제거를 수행 할 수 있습니다 . 불변 구조체를 현명하게 사용하면 refcounted 클래스보다 성능이 뛰어납니다.

최신 정보

@Joseph가 이것이 이유를 제공하지 않는다고 주장했듯이 조금 더 추가하고 있습니다.

구조체에는 두 가지 방법이 있습니다. 일반돌연변이 방법. 일반 메서드는 불변 (또는 비변이)을 의미 합니다. 이 분리는 불변의 의미 를 지원하기 위해서만 존재합니다 . 불변 모드의 객체는 그 상태를 전혀 변경하지 않아야합니다.

그런 다음 불변 메서드는 이러한 의미 불변성을 보장해야합니다 . 즉, 내부 값을 변경해서는 안됩니다. 따라서 컴파일러는 변경 불가능한 메서드에서 자체 상태 변경을 허용하지 않습니다. 반대로, mutating 메서드는 상태를 자유롭게 수정할 수 있습니다.

그러면 왜 불변성이 기본값인지 궁금 할 것입니다 . 왜냐하면 돌연변이 가치의 미래 상태를 예측하기가 매우 어렵고 일반적으로 두통과 버그의 주요 원인이되기 때문입니다. 많은 사람들이 솔루션이 변경 가능한 항목을 피하고 있다는 데 동의 했으며 기본적 으로 변경 불가능 은 C / C ++ 계열 언어 및 그 파생어에서 수십 년 동안 위시리스트의 맨 위에있었습니다.

자세한 내용은 순전히 기능적인 스타일 을 참조하십시오. 어쨌든, 우리는 여전히 변경 가능한 것들이 필요합니다. 왜냐하면 불변의 것들은 약간의 약점이 있기 때문입니다. 그리고 이것에 대해 논의하는 것은 주제에서 벗어난 것 같습니다.

이게 도움이 되길 바란다.


구조는 필드의 집합입니다. 특정 구조 인스턴스가 변경 가능한 경우 해당 필드는 변경 가능합니다. 인스턴스가 변경 불가능한 경우 해당 필드는 변경 불가능합니다. 따라서 특정 인스턴스의 필드가 변경 가능하거나 변경 불가능할 수있는 가능성에 대비하여 구조 유형을 준비해야합니다.

In order for a structure method to mutate the fields of the underlying struct, those fields have to be mutable. If a method that mutates fields of the underlying struct is invoked upon an immutable structure, it would try to mutate immutable fields. Since nothing good could come of that, such invocation needs to be forbidden.

To achieve that, Swift divides structure methods into two categories: those that modify the underlying structure, and thus may only be invoked on mutable structure instances, and those that do not modify the underlying structure and should thus be invokable on both mutable and immutable instances. The latter usage is probably the more frequent, and is thus the default.

By comparison, .NET presently (still!) offers no means of distinguishing structure methods that modify the structure from those that don't. Instead, invoking a structure method on an immutable structure instance will cause the compiler to make a mutable copy of the structure instance, let the method do whatever it wants with it, and discard the copy when the method is done. This has the effect of forcing the compiler to waste time copying the structure whether or not the method modifies it, even though adding the copy operation will almost never turn what would be semantically-incorrect code into semantically-correct code; it will merely cause code that is semantically wrong in one way (modifying an "immutable" value) to be wrong in a different way (allowing code to think it's modifying a structure, but discarding the attempted changes). Allowing struct methods to indicate whether they will modify the underlying structure can eliminate the need for a useless copy operation, and also ensures that attempted erroneous usage will get flagged.


Caution: layman's terms ahead.

This explanation isn't rigorously correct at the most nitty-gritty code level. However it has been reviewed by a guy who actually works on Swift and he said it's good enough as a basic explanation.

So I want to try to simply and directly answer the question of "why".

To be precise: why do we have to mark struct functions as mutating when we can change struct parameters without any modifying keywords?

So, big picture, it has a lot to do with the philosophy that keeps Swift swift.

You could kind of think of it like the problem of managing actual physical addresses. When you change your address, if there are a lot of people who have your current one, you have to notify all of them that you've moved. But if no one has your current address, you can just move wherever you want, and no one needs to know.

In this situation, Swift is kind of like the post office. If lots of people with lots of contacts move around a lot, it has a really high overhead. It has to pay a big staff of people to handle all those notifications, and the process takes up a lot of time and effort. That's why Swift's ideal state is for everyone in its town to have as few contacts as possible. Then it doesn't need a big staff for handling address changes, and it can do everything else faster and better.

This is also why Swift-folks are all raving on about value types vs. reference types. By nature, reference types rack up "contacts" all over the place, and value types usually don't need more than a couple. Value types are "Swift"-er.

So back to small picture: structs. Structs are a big deal in Swift because they can do most of the things objects can do, but they're value types.

Let's continue the physical address analogy by imagining a misterStruct that lives in someObjectVille. The analogy gets a little wonked up here, but I think it still is helpful.

So to model changing a variable on a struct, let's say misterStruct has green hair, and gets an order to switch to blue hair. The analogy gets wonked, like I said, but sort of what happens is that instead of changing misterStruct's hair, the old person moves out and a new person with blue hair moves in, and that new person begins calling themselves misterStruct. No one needs to get a change of address notification, but if anyone looks at that address, they'll see a guy with blue hair.

Now let's model what happens when you call a function on a struct. In this case, it's like misterStruct gets an order such as changeYourHairBlue(). So the post office delivers the instruction to misterStruct "go change your hair to blue and tell me when you're done."

If he's following the same routine as before, if he's doing what he did when the variable was changed directly, what misterStruct will do is move out of his own house and call in a new person with blue hair. But that's the problem.

The order was "go change your hair to blue and tell me when you're done," but it's the green guy who got that order. After the blue guy moves in, a "job complete" notification still has to be sent back. But the blue guy knows nothing about it.

[To really wonk up this analogy something awful, what technically happened to green-haired guy was that after he moved out, he immediately committed suicide. So he can't notify anyone that the task is complete either!]

To avoid this problem, in cases like this only, Swift has to go in directly to the house at that address and actually change the current inhabitant's hair. That is a completely different process than just sending in a new guy.

And that's why Swift wants us to use the mutating keyword!

The end result looks the same to anything that has to refer to the struct: the inhabitant of the house now has blue hair. But the processes for achieving it are actually completely different. It looks like it's doing the same thing, but it's doing a very different thing. It's doing a thing that Swift structs in general never do.

So to give the poor compiler a little help, and not make it have to figure out whether a function mutates the struct or not, on its own, for every single struct function ever, we are asked to have pity and use the mutating keyword.

In essence, to help Swift stay swift, we all must do our part. :)

EDIT:

Hey dude/dudette who downvoted me, I just completely rewrote my answer. If it sits better with you, will you remove the downvote?


Swift structs can be instantiated as either constants (via let) or variables (via var)

Consider Swift's Array struct (yes it's a 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

Why didn't the append work with the planet names? Because append is marked with the mutating keyword. And since planetNames was declared using let, all methods thus marked are off limits.

In your example the compiler can tell you're modifying the struct by assigning to one or more of its properties outside of an init. If you change your code a bit you'll see that x and y aren't always accessible outside the struct. Notice the let on the first line.

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

Consider an analogy with C++. A struct method in Swift being mutating/not-mutating is analogous to a method in C++ being non-const/const. A method marked const in C++ similarly cannot mutate the struct.

You can change a var from outside a struct, but you cannot change it from its own methods.

In C++, you can also "change a var from outside the struct" -- but only if you have a non-const struct variable. If you have a const struct variable, you cannot assign to a var, and you also cannot call a non-const method. Similarly, in Swift, you can change a property of a struct only if the struct variable is not a constant. If you have a struct constant, you cannot assign to a property, and you also cannot call a mutating method.


I wondered the same thing when I started learning Swift, and each of these answers, while adding some insights perhaps, are kind of wordy and confusing in their own right. I think the answer to your question is actually pretty simple…

The mutating method defined inside your struct wants permission to modify each and every instance of itself that will ever be created in the future. What if one of those instances gets assigned to an immutable constant with let? Uh oh. To protect you from yourself (and to let the editor and compiler know what you're trying to do), you're forced to be explicit when you want to give an instance method this sort of power.

By contrast, the setting of a property from outside your struct is operating on a known instance of that struct. If it's assigned to a constant, Xcode will let you know about it the moment you've typed in the method call.

This is one of the things I'm loving about Swift as I start to use it more—being alerted to errors as I type them. Sure beats the heck out of troubleshooting obscure JavaScript bugs!

참고URL : https://stackoverflow.com/questions/24035648/swift-and-mutating-struct

반응형