신속하고 변형 된 구조체


97

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) 

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

답변:


81

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

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

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

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

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

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

최신 정보

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

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

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

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

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

이게 도움이 되길 바란다.


2
따라서 기본적으로 다음과 같이 해석 할 수 있습니다. 구조체는 변경 가능 여부를 미리 알지 못하므로 다르게 명시하지 않는 한 불변성을 가정합니다. 정말 말이되는 것처럼 보였습니다. 이 올바른지?
Mike Seghers 2014-06-04

1
@MikeSeghers 그게 말이되는 것 같아요.
언일 2014-06-04

3
이것이 지금까지 두 가지 답변 중 최고이지만, 기본적으로 값 유형의 속성을 인스턴스 메서드 내에서 수정할 수없는 이유에 대해서는 대답하지 않습니다. 당신이 구조체는 불변으로 기본 있기 때문에 그 것을 암시합니다,하지만 난 그게 사실이라고 생각 해달라고
조셉 나이트

4
@JosephKnight 구조체가 불변으로 기본 설정된 것은 아닙니다. 구조체의 메서드는 기본적으로 불변으로 설정되어 있습니다. 반대로 가정하는 C ++처럼 생각하십시오. C ++ 메서드의 기본값은 non constant this입니다 const. 'const this'를 사용하고 상수 인스턴스에서 허용하려면 메서드 에 a를 명시 적으로 추가 해야합니다 (인스턴스가 const 인 경우 일반 메서드를 사용할 수 없음). Swift는 반대의 기본값으로 똑같이합니다 : 메소드는 기본적으로 'const this'를 가지고 있으며 상수 인스턴스에 대해 금지되어야하는 경우 그렇지 않으면 표시합니다.
Analog File

3
@golddove 예, 할 수 없습니다. 해서는 안됩니다. 항상 새로운 버전의 가치를 만들거나 파생시켜야하며 프로그램은 이러한 방식을 의도적으로 채택하도록 설계되어야합니다. 이러한 우발적 인 돌연변이를 방지하기위한 기능이 있습니다. 다시 말하지만 이것은 함수형 프로그래밍 의 핵심 개념 중 하나입니다 .
eonil

25

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

구조체 메서드가 기본 구조체의 필드를 변경하려면 해당 필드가 변경 가능해야합니다. 기본 구조체의 필드를 변경하는 메서드가 변경 불가능한 구조에 대해 호출되면 변경 불가능한 필드를 변경하려고합니다. 좋은 것은 아무것도 나오지 않기 때문에 그러한 호출은 금지되어야합니다.

이를 달성하기 위해 Swift는 구조 메서드를 두 가지 범주로 나눕니다. 즉, 기본 구조를 수정하여 변경 가능한 구조 인스턴스에서만 호출 할 수있는 것과 기본 구조를 수정하지 않으므로 변경 가능한 인스턴스와 불변 인스턴스 모두에서 호출 할 수 있어야합니다. . 후자의 사용이 더 자주 사용되므로 기본값입니다.

이에 비해 .NET은 현재 (여전히!) 구조를 수정하는 구조 메서드와 그렇지 않은 메서드를 구별하는 수단을 제공하지 않습니다. 대신 불변 구조 인스턴스에서 구조 메서드를 호출하면 컴파일러가 구조 인스턴스의 가변 복사본을 만들고 메서드가 원하는대로 수행하도록하고 메서드가 완료되면 복사본을 버립니다. 이것은 메소드가 구조를 수정하든 그렇지 않든 컴파일러가 구조를 복사하는 데 시간을 낭비하게하는 효과가 있습니다. 비록 복사 작업을 추가해도 의미 상 잘못된 코드를 의미 상 올바른 코드로 바꾸는 일은 거의 없습니다. 한 가지 방식으로 의미 상 잘못된 코드 ( "불변"값 수정)가 다른 방식으로 잘못되게 할뿐입니다 (코드가 구조를 수정하고 있다고 생각하도록 허용하고, 그러나 시도한 변경 사항을 무시합니다). struct 메서드가 기본 구조를 수정할지 여부를 나타내도록 허용하면 쓸모없는 복사 작업이 필요하지 않으며 잘못된 사용 시도가 플래그가 지정되도록 할 수 있습니다.


1
.NET과의 비교와 mutating 키워드가없는 예제는 나에게 분명했습니다. mutating 키워드가 이익을 창출하는 이유.
Binarian

20

주의 : 평신도의 조건이 앞서 있습니다.

이 설명은 가장 핵심적인 코드 수준에서 엄격하게 정확하지 않습니다. 그러나 실제로 Swift에서 작업하는 사람이 검토했으며 기본 설명으로 충분하다고 말했습니다.

그래서 저는 "왜"라는 질문에 간단하고 직접적으로 대답 하려고합니다 .

정확히 말하면, 키워드를 수정하지 않고 구조체 매개 변수를 변경할 수있을 때 구조체 함수를 표시해야하는 이유는 mutating무엇입니까?

따라서 큰 그림은 Swift를 신속하게 유지하는 철학과 많은 관련이 있습니다.

실제 물리적 주소를 관리하는 문제처럼 생각할 수 있습니다. 주소를 변경할 때 현재 주소를 가지고있는 사람이 많은 경우 이사 한 사실을 모든 사람에게 알려야합니다. 그러나 현재 주소가없는 경우 원하는 곳으로 이동할 수 있으며 아무도 알 필요가 없습니다.

이 상황에서 Swift는 우체국과 비슷합니다. 연락처가 많은 사람들이 많이 움직이면 오버 헤드가 정말 높아집니다. 이러한 모든 알림을 처리하려면 많은 직원에게 비용을 지불해야하며 프로세스에는 많은 시간과 노력이 필요합니다. 그렇기 때문에 Swift의 이상적인 상태는 도시의 모든 사람이 가능한 한 적은 연락처를 갖는 것입니다. 그러면 주소 변경을 처리하는 데 큰 직원이 필요하지 않으며 다른 모든 작업을 더 빠르고 효율적으로 수행 할 수 있습니다.

이것이 바로 Swift 사용자들이 모두 값 유형과 참조 유형에 대해 열광하는 이유이기도합니다. 본질적으로 참조 유형은 모든 곳에서 "연락처"를 쌓으며 값 유형은 일반적으로 두 개 이상을 필요로하지 않습니다. 값 유형은 "Swift"-er입니다.

다시 작은 그림으로 돌아갑니다 : structs. 구조체는 객체가 할 수있는 대부분의 일을 할 수 있기 때문에 Swift에서 큰 문제이지만 값 유형입니다.

misterStruct사는 a 를 상상하여 물리적 주소 비유를 계속합시다 someObjectVille. 비유는 여기에서 약간 놀랐지 만 여전히 도움이된다고 생각합니다.

따라서에서 변수를 변경하는 모델을 위해 녹색 머리카락이 있고 파란색 머리카락으로 전환하라는 명령을 struct받았다고 가정 해 보겠습니다 misterStruct. 제가 말했듯이 비유는 놀랍습니다.하지만 어떤 일이 일어나는 것은 misterStruct머리카락 을 바꾸는 대신 노인이 나가고 파란 머리를 가진 새로운 사람이 들어오고 새로운 사람이 자신을 부르기 시작한다는 것 misterStruct입니다. 누구도 주소 변경 알림을받을 필요가 없지만 누군가 그 주소를 보면 파란 머리를 가진 남자를 보게 될 것입니다.

이제 .NET에서 함수를 호출 할 때 어떤 일이 발생하는지 모델링 해 보겠습니다 struct. 이 경우 같은 misterStruct주문을받는 것과 같습니다 changeYourHairBlue(). 그래서 우체국에서는 misterStruct"머리를 파란색으로 바꾸고 다 끝나면 알려주세요."라는 지시를 내립니다.

그는 이전과 같은 루틴을 다음 있다면, 만약 그 변수가 직접 변경할 때 그가 무슨 짓을하고있어 misterStruct할 것입니다 것은 자신의 집으로 이사 파란 머리와 함께 새로운 사람과 전화. 하지만 그게 문제입니다.

명령은 "머리를 파란색으로 바꾸고 다 끝나면 알려주세요"였지만 그 명령을받은 것은 녹색 사람입니다. 파란색 사람이 들어온 후에도 "작업 완료"알림을 다시 보내야합니다. 하지만 파란 남자는 그것에 대해 아무것도 모릅니다.

[이 비유를 정말 끔찍하게 생각하기 위해, 기술적으로 초록 머리 남자에게 일어난 일은 그가 이사 한 후 즉시 자살했다는 것입니다. 따라서 그는 작업이 완료되었음을 누구에게도 알릴 수 없습니다 ! ]

이 문제를 피하기 위해, 이와 같은 경우 에만 Swift는 해당 주소의 집으로 직접 가서 현재 거주자의 머리카락을 실제로 변경해야합니다 . 그것은 새로운 사람을 보내는 것과는 완전히 다른 과정입니다.

그래서 Swift는 우리가 mutating키워드 를 사용하기를 원합니다 !

최종 결과는 구조체를 참조해야하는 모든 항목에서 동일하게 보입니다. 이제 집의 거주자는 파란색 머리카락을가집니다. 그러나 그것을 달성하는 과정은 실제로 완전히 다릅니다. 똑같은 일을하는 것 같지만 아주 다른 일을하고 있습니다. 그것은하는 것하고있어 일반적으로 신속한 구조체는하지 않을입니다.

따라서 빈약 한 컴파일러에게 약간의 도움을주고 함수 가 모든 단일 구조체 함수struct 에 대해 자체적으로 변형 여부를 알아낼 필요가 없도록하기 위해 동정심을 갖고 mutating키워드를 사용해야합니다 .

본질적으로 Swift가 신속하게 유지 될 수 있도록 우리 모두는 우리의 역할을 수행해야합니다. :)

편집하다:

저를 비추천 한 친구 / 친구 야, 내 대답을 완전히 다시 작성했습니다. 그것이 당신에게 더 잘 어울린다면, 당신은 반대표를 제거 할 것입니까?


1
이것은 너무 <f-word-here> 놀랍게 쓰여졌습니다! 그래서 이해하기 쉽습니다. 나는 이것을 위해 인터넷을 샅샅이 뒤졌고 여기에 대답이 있습니다 (어쨌든 소스 코드를 거치지 않고도). 시간을내어 작성해 주셔서 감사합니다.
Vaibhav

1
내가 올바르게 이해하고 있는지 확인하고 싶습니다. Struct 인스턴스의 속성이 Swift에서 직접 변경되면 Struct의 인스턴스가 "종료"되고 변경된 속성이있는 새 인스턴스가 "생성"된다고 말할 수 있습니까? 무대 뒤에서? 그리고 인스턴스 내에서 변경 방법을 사용하여 해당 인스턴스의 속성을 변경하면이 종료 및 재 초기화 프로세스가 발생하지 않습니까?
테드

@Teo, 내가 확실하게 대답 할 수 있으면 좋겠지 만, 내가 말할 수있는 가장 좋은 점은 실제 Swift 개발자를 지나서이 비유를 실행했고 그들은 "기본적으로 정확합니다"라고 말하면서 기술적 인 의미에서 더 많은 것이 있음을 암시합니다. 그러나 그 이해와 함께 – 이것은 단지 이것보다 더 많은 것이 있음 – 넓은 의미에서, 그렇습니다, 이것이이 비유가 말하는 것입니다.
Le Mot Juiced

8

Swift 구조체는 상수 (를 통해 let) 또는 변수 (를 통해 var) 로 인스턴스화 할 수 있습니다.

Swift의 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

5

C ++의 비유를 고려하십시오. Swift의 구조체 메서드가 mutating/ not- 인 mutating것은 C ++의 메서드가 const/ 가 아닌 것과 유사합니다 const. const비슷하게 C ++ 로 표시된 메서드 는 구조체를 변경할 수 없습니다.

구조체 외부에서 var를 변경할 수 있지만 자체 메서드에서는 변경할 수 없습니다.

C ++에서는 "구조체 외부에서 변수를 변경"할 수도 있습니다. 그러나 비 구조체 변수 가있는 경우 에만 가능합니다 const. const구조체 변수 가있는 경우 var에 할당 할 수 없으며 비 const메서드도 호출 할 수 없습니다 . 마찬가지로 Swift에서는 struct 변수가 상수가 아닌 경우에만 struct의 속성을 변경할 수 있습니다. 구조체 상수가있는 경우 속성에 할당 할 수 없으며 mutating메서드를 호출 할 수도 없습니다 .


1

Swift를 배우기 시작했을 때도 똑같은 것이 궁금했습니다. 이러한 각 답변은 아마도 약간의 통찰력을 추가하는 동안 그 자체로 장황하고 혼란 스러울 것입니다. 귀하의 질문에 대한 답은 실제로 매우 간단하다고 생각합니다.

구조체 내부에 정의 된 mutating 메서드 는 미래에 생성 될 모든 인스턴스를 수정할 수있는 권한을 원합니다. 그 인스턴스 중 하나가 불변 상수에 할당되면 let어떻게 될까요? 어 오. 자신으로부터 자신을 보호하기 위해 (그리고 에디터와 컴파일러가 무엇을 하려고 하는지 알기 위해) 인스턴스 메소드에 이런 종류의 권한을 부여하고 싶을 때 명시 적으로 지정해야합니다.

반대로, 구조체 외부 의 속성 설정 은 해당 구조체의 알려진 인스턴스에서 작동합니다. 상수에 할당 된 경우 Xcode는 메서드 호출에 입력 한 순간이를 알려줍니다.

이것은 내가 Swift를 더 많이 사용하기 시작할 때 내가 좋아하는 것 중 하나입니다. 입력 할 때 오류에 대해 경고를받습니다. 확실하지 않은 JavaScript 버그를 해결하는 것보다 훨씬 뛰어납니다!


1

SWIFT : Structs에서 mutating 함수 사용

Swift 프로그래머는 Struct 메소드 내에서 속성을 수정할 수없는 방식으로 Structs를 개발했습니다. 예를 들어, 아래 주어진 코드를 확인하십시오.

struct City
{
  var population : Int 
  func changePopulation(newpopulation : Int)
  {
      population = newpopulation //error: cannot modify property "popultion"
  }
}
  var mycity = City(population : 1000)
  mycity.changePopulation(newpopulation : 2000)

위 코드를 실행하면 Struct City의 부동산 인구에 새 값을 할당하려고하기 때문에 오류가 발생합니다. 기본적으로 Structs 속성은 자체 메서드 내에서 변경할 수 없습니다. 이것이 Apple 개발자가 빌드 한 방법이므로 Structs는 기본적으로 정적 특성을 갖습니다.

어떻게 해결합니까? 대안은 무엇입니까?

변형 키워드 :

함수를 Struct 내부에서 mutating으로 선언하면 Structs의 속성을 변경할 수 있습니다. Line No : 5, 위 코드 중 다음과 같이 변경됩니다.

mutating changePopulation(newpopulation : Int)

이제 메서드 범위 내에서 newpopulation 의 값 을 속성 모집단 에 할당 할 수 있습니다 .

노트 :

let mycity = City(1000)     
mycity.changePopulation(newpopulation : 2000)   //error: cannot modify property "popultion"

Struct 객체에 var 대신 let을 사용하면 속성 값을 변경할 수 없습니다. 또한 let 인스턴스를 사용하여 mutating 함수를 호출하려고 할 때 오류가 발생하는 이유입니다. 따라서 속성 값을 변경할 때마다 var를 사용하는 것이 좋습니다.

여러분의 의견과 생각을 듣고 싶습니다… ..

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.