왜 수업보다 구조를 선택해야합니까?


476

Java 배경에서 나온 Swift를 가지고 놀면서 왜 Class 대신 Struct를 선택하고 싶습니까? 더 적은 기능을 제공하는 Struct와 동일한 기능인 것 같습니다. 왜 그것을 선택해야합니까?


11
구조는 코드에서 전달 될 때 항상 복사되며 참조 카운팅을 사용하지 않습니다. 출처 : developer.apple.com/library/prerelease/ios/documentation/swift/…
holex

4
구조체가 논리가 아닌 데이터를 보유하는 데 더 적합하다고 말하고 싶습니다. Java 용어로 말하면 구조체를 "값 객체"라고 상상해보십시오.
Vincent Guerci 2016 년

6
이 전체 대화에 놀랍니다 . 복사 중 일명 게으른 사본 대한 언급이 없습니다 . 구조체 복사 성능에 대한 우려는 대부분이 디자인으로 인해 약점입니다.
David James

3
수업에서 구조체를 선택하는 것은 의견의 문제가 아닙니다. 하나 또는 다른 것을 선택해야하는 특별한 이유가 있습니다.
David James

왜 Array가 threadSafe가 아닌지 확인하는 것이 좋습니다 . Arrays & Structs가 모두 값 유형이기 때문에 관련이 있습니다. 여기에있는 모든 대답은 구조체 / 배열 / 값 유형을 사용하면 스레드 안전 문제가 발생하지 않지만 코너 사례가 있다고 언급합니다.
Honey

답변:


548

Swift에서 널리 사용되는 WWDC 2015 토크 프로토콜 지향 프로그래밍 ( video , transcript )에 따르면 Swift는 여러 상황에서 클래스보다 구조를 개선하는 여러 기능을 제공합니다.

클래스에서와 같이 동일한 인스턴스에 대한 여러 참조를 갖는 것보다 복사가 훨씬 안전하기 때문에 Structs가 비교적 작고 복사 가능한 경우 선호됩니다. 변수를 여러 클래스 및 / 또는 멀티 스레드 환경으로 전달할 때 특히 중요합니다. 변수 사본을 항상 다른 장소로 보낼 수 있다면 다른 장소가 변수 값을 변경하는 것에 대해 걱정할 필요가 없습니다.

Structs를 사용하면 변수의 단일 인스턴스에 액세스 / 수정하기 위해 메모리 누수 또는 여러 스레드 경주에 대해 걱정할 필요가 없습니다. (기술적으로 생각하면 클로저 내부의 구조체를 캡처 할 때 예외적으로 명시 적으로 복사하도록 표시하지 않으면 인스턴스에 대한 참조를 캡처하기 때문에 예외입니다.)

클래스는 단일 수퍼 클래스에서만 상속 할 수 있기 때문에 클래스가 부풀어 질 수 있습니다. 그것은 우리가 느슨하게 관련된 많은 다른 능력들을 포함하는 거대한 수퍼 클래스를 만들도록 권장합니다. 특히 프로토콜에 구현을 제공 할 수있는 프로토콜 확장과 함께 프로토콜을 사용하면 이러한 종류의 동작을 달성하기 위해 클래스가 필요하지 않습니다.

이 강의는 수업이 선호되는 다음 시나리오를 설명합니다.

  • 인스턴스 복사 또는 비교가 의미가 없습니다 (예 : Window)
  • 인스턴스 수명은 외부 효과 (예 : TemporaryFile)와 연결되어 있습니다.
  • 인스턴스는 "싱크"일뿐입니다. 쓰기 전용 도관을 외부 상태 (예 : CGContext)로

구조체가 기본값이어야하고 클래스는 폴백이어야 함을 의미합니다.

반면에 Swift Programming Language 문서는 다소 모순됩니다.

구조 인스턴스는 항상 값으로 전달되고 클래스 인스턴스는 항상 참조로 전달됩니다. 이것은 그들이 다른 종류의 작업에 적합하다는 것을 의미합니다. 프로젝트에 필요한 데이터 구조 및 기능을 고려할 때 각 데이터 구조를 클래스 또는 구조로 정의해야하는지 결정하십시오.

일반적인 지침으로 다음 조건 중 하나 이상이 적용될 때 구조를 만드는 것이 좋습니다.

  • 구조의 주요 목적은 비교적 간단한 몇 가지 데이터 값을 캡슐화하는 것입니다.
  • 해당 구조의 인스턴스를 할당하거나 전달할 때 캡슐화 된 값이 참조되지 않고 복사 될 것으로 예상하는 것이 합리적입니다.
  • 구조에 의해 저장된 모든 속성은 자체적으로 값 형식이며 참조가 아닌 복사 될 것으로 예상됩니다.
  • 구조는 기존의 다른 유형에서 속성이나 동작을 상속 할 필요가 없습니다.

구조에 적합한 후보의 예는 다음과 같습니다.

  • 기하학적 유형의 크기로, Double 유형의 너비 속성과 높이 속성을 캡슐화 할 수 있습니다.
  • Int 유형의 시작 속성과 길이 속성을 캡슐화하여 계열 내의 범위를 참조하는 방법입니다.
  • 3D 좌표 시스템의 점으로, 각각 Double 유형의 x, y 및 z 속성을 캡슐화 할 수 있습니다.

다른 모든 경우에는 클래스를 정의하고 참조로 관리 및 전달할 해당 클래스의 인스턴스를 작성하십시오. 실제로 이것은 대부분의 사용자 정의 데이터 구조가 구조가 아닌 클래스 여야 함을 의미합니다.

여기서 우리는 기본적으로 클래스를 사용하고 특정 상황에서만 구조를 사용해야한다고 주장합니다. 궁극적으로 가치 유형과 참조 유형의 실제 의미를 이해해야하며, 언제 구조체 또는 클래스를 사용할 지에 대한 정보에 근거한 결정을 내릴 수 있습니다. 또한 이러한 개념은 항상 발전하고 있으며 Swift Programming Language 문서는 Protocol Oriented Programming 강연 전에 작성되었습니다.


12
이 글의 요점은 기본적으로 구조체를 선택해야하며 클래스는 필요할 때만 사용해야한다는 것입니다. Structs는 특히 멀티 스레드 환경에서 훨씬 안전하고 버그가 없습니다. 예, 구조체 대신 항상 클래스를 사용할 수 있지만 구조체가 바람직합니다.
drewag

16
@drewag 그것이 말하는 것과 정확히 반대 인 것 같습니다. 그것은 클래스가 구조가 아닌 사용하는 기본값이어야한다고 말하고 있습니다. In practice, this means that most custom data constructs should be classes, not structures.읽은 후에 대부분의 데이터 세트가 클래스가 아닌 구조가되는 방법을 설명해 줄 수 있습니까? 그들은 무언가가 구조체가되어야 할 때 특정한 규칙을 제시했고 "클래스가 더 나은 다른 모든 시나리오"라고 거의 말 했어요.
Matt

42
마지막 줄은 "내 개인적인 조언은 문서와 반대입니다."라고 말해야합니다. 그러면 훌륭한 답변입니다!
Dan Rosenstark

5
Swift 2.2 책은 여전히 ​​대부분의 상황에서 클래스 사용을 말합니다.
David James

6
클래스 위의 구조는 분명히 복잡성을 줄입니다. 그러나 구조체가 기본 선택이 될 때 메모리 사용에 미치는 영향은 무엇입니까? 사물이 참조 대신 모든 곳에서 복사되면 앱의 메모리 사용량이 증가해야합니다. 그렇지 않습니까?
MadNik

164

구조체 인스턴스는 스택에 할당되고 클래스 인스턴스는 힙에 할당되므로 구조체가 때로는 더 빠를 수 있습니다.

그러나 항상 직접 측정하고 고유 한 사용 사례에 따라 결정해야합니다.

and를 Int사용하여 데이터 유형 을 래핑하는 두 가지 전략을 보여주는 다음 예를 고려하십시오 . 나는 여러 개의 필드가있는 실제 세계를 더 잘 반영하기 위해 10 개의 반복 값을 사용하고 있습니다.structclass

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}

func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

성능은 다음을 사용하여 측정됩니다.

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}

// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}

func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()

    block()

    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

코드는 https://github.com/knguyen2708/StructVsClassPerformance 에서 찾을 수 있습니다.

업데이트 (2018 년 3 월 27 일) :

Swift 4.0, Xcode 9.2에서 iPhone 6S, iOS 11.2.6에서 릴리스 빌드를 실행하면 Swift Compiler 설정은 -O -whole-module-optimization다음과 같습니다.

  • class 버전은 2.06 초 걸렸다
  • struct 버전은 4.17e-08 초가 걸렸습니다 (50,000,000 배 더 빠름)

(분산이 5 % 미만으로 매우 작기 때문에 더 이상 여러 번의 런을 수행하지 않습니다)

참고 : 전체 모듈 최적화가 없으면 차이가 훨씬 덜 극적입니다. 누군가가 깃발이 실제로하는 일을 지적 할 수 있다면 기쁠 것입니다.


업데이트 (2016 년 5 월 7 일) :

Swift 2.2.1, Xcode 7.3에서 iPhone 6S, iOS 9.3.1에서 릴리스 빌드를 실행하면 평균 5 회 이상 실행되며 Swift Compiler 설정은 -O -whole-module-optimization다음과 같습니다.

  • class 버전은 2.159942142s 걸렸다
  • struct 버전은 5.83E-08s (37,000,000 배 더 빠름)

참고 : 누군가가 실제 시나리오에서 구조체에 필드가 두 개 이상있을 것이라고 언급 했으므로 1 대신 10 필드로 구조체 / 클래스에 대한 테스트를 추가했습니다. 놀랍게도 결과는 크게 다르지 않습니다.


원래 결과 (2014 년 6 월 1 일) :

(10이 아닌 1 필드의 구조체 / 클래스에 의존)

Swift 1.2부터 Xcode 6.3.2, iPhone 5S, iOS 8.3에서 릴리스 빌드 실행 중 평균 5 회 이상 실행

  • class 버전 9.788332333s 소요
  • struct 버전은 0.010532942 초 (900 배 빨라짐)

이전 결과 (알 수없는 시간부터)

(10이 아닌 1 필드의 구조체 / 클래스에 의존)

MacBook Pro에 릴리스 빌드가있는 경우 :

  • class버전은 1.10082 초 걸렸다
  • struct버전 (50 배 빠른) 0.02324 초를했다

27
사실, 많은 구조체를 복사하는 것이 단일 객체에 대한 참조를 복사하는 것보다 느릴 것 같습니다. 즉, 임의로 큰 메모리 블록을 복사하는 것보다 단일 포인터를 복사하는 것이 더 빠릅니다.
Tylerc230

14
-1이 테스트는 구조체에 단 하나의 var 만 있기 때문에 좋은 예가 아닙니다. 여러 값과 객체를 추가하면 구조체 버전이 클래스 버전과 비슷해집니다. 더 많은 변수를 추가할수록 구조체 버전이 느려집니다.
joshrl

6
@ joshrl은 당신의 요점을 얻었지만 예는 "좋은"것인지 또는 특정 상황에 달려 있습니다. 이 코드는 내 앱에서 추출되었으므로 유효한 사용 사례이며 구조체를 사용하면 내 앱의 성능이 크게 향상되었습니다. 아마도 일반적인 사용 사례는 아닐 것입니다 (일반적인 사용 사례는 대부분의 앱에서 병목 현상이 다른 곳에서 발생하기 때문에 데이터가 얼마나 빨리 전달 될 수 있는지에 신경 쓰지 않습니다. 예를 들어 네트워크 연결과 같은 경우 최적화는 그렇지 않습니다) GB 또는 RAM이있는 GHz 장치가있는 경우 중요합니다.
Khanh Nguyen

26
내가 신속하게 복사하는 것은 쓰기 시간에 발생하도록 최적화되어 있습니다. 즉, 새 사본을 변경하지 않으면 실제 메모리 사본이 없습니다.
Matjan

6
이 대답은 비현실적이며 많은 경우에 부정확하다는 점에서 매우 사소한 예를 보여줍니다. 더 좋은 대답은 "의존"입니다.
iwasrobbed에

60

구조체와 클래스의 유사점

간단한 예제로 이것에 대한 요점을 만들었습니다. https://github.com/objc-swift/swift-classes-vs-structures

그리고 차이점

1. 상속.

구조는 신속하게 상속받을 수 없습니다. 네가 원한다면

class Vehicle{
}

class Car : Vehicle{
}

수업에 가십시오.

2. 통과

스위프트 구조는 값으로 전달되고 클래스 인스턴스는 참조로 전달됩니다.

상황에 따른 차이

구조 상수 및 변수

예 (WWDC 2014에서 사용)

struct Point{

   var x = 0.0;
   var y = 0.0;

} 

Point라는 구조체를 정의합니다.

var point = Point(x:0.0,y:2.0)

이제 x를 변경하려고하면 유효한 표현입니다.

point.x = 5

그러나 점을 상수로 정의하면.

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

이 경우 전체 포인트는 불변 상수입니다.

클래스 클래스를 대신 사용하면 유효한 표현식입니다. 클래스 불변 상수는 인스턴스 변수가 아닌 클래스 자체에 대한 참조이기 때문에 (변수가 상수로 정의되지 않은 경우)


당신은 스위프트의 구조체를 상속 할 수 gist.github.com/AliSoftware/9e4946c8b6038572d678
thatguy

12
위의 요점은 구조체의 상속 풍미를 얻는 방법에 관한 것입니다. 당신은 같은 구문을 볼 수 있습니다. A : B. A라고하는 구조체입니다. B라는 프로토콜을 구현합니다. Apple 문서에서는 구조체가 순수한 상속을 지원하지 않으며 그렇지 않다고 분명히 언급합니다.
MadNik

2
당신의 마지막 단락은 위대했습니다. 나는 항상 당신이 상수를 바꿀 수 있다는 것을 알고 있었지만 때때로 나는 당신이 할 수 없었던 곳을보고 당황했습니다. 이 특징으로 인해 눈에 띄게되었습니다
Honey

28

고려해야 할 다른 이유는 다음과 같습니다.

  1. 구조체는 코드에서 전혀 유지 관리 할 필요가없는 자동 이니셜 라이저를 얻습니다.

    struct MorphProperty {
       var type : MorphPropertyValueType
       var key : String
       var value : AnyObject
    
       enum MorphPropertyValueType {
           case String, Int, Double
       }
     }
    
     var m = MorphProperty(type: .Int, key: "what", value: "blah")

클래스에서 이것을 얻으려면 초기화 프로그램을 추가하고 초기화 기를 유지해야 합니다 ...

  1. 기본 컬렉션 유형 Array은 구조체입니다. 자신의 코드에서 더 많이 사용할수록 참조가 아닌 값으로 전달하는 데 더 익숙해집니다. 예를 들어 :

    func removeLast(var array:[String]) {
       array.removeLast()
       println(array) // [one, two]
    }
    
    var someArray = ["one", "two", "three"]
    removeLast(someArray)
    println(someArray) // [one, two, three]
  2. 명백하게 불변성 vs. 가변성은 큰 주제이지만, 많은 똑똑한 사람들은 불변성 (이 경우 구조체)이 바람직하다고 생각합니다. 가변 대 불변 개체


4
자동 이니셜 라이저를 얻는 것이 사실입니다. 또한 모든 속성이 선택 사항 인 경우 빈 이니셜 라이저가 나타납니다. 그러나 Framework에 구조체가있는 경우 internal범위 밖에서 사용할 수 있으려면 실제로 초기자를 직접 작성해야합니다 .
Abizern

2
@Abizern은 stackoverflow.com/a/26224873/8047을 확인했으며 성가신 사람입니다.
Dan Rosenstark

2
@Abizern Swift에는 모든 것이 중요한 이유가 있지만, 어떤 곳에서는 무언가가 사실 일 때마다 다른 곳에서는 그렇지 않을 때마다 개발자는 더 많은 것을 알아야합니다. 나는 여기서 "그런 어려운 언어로 일하게되어 기쁩니다!"라고 말해야한다고 생각합니다.
Dan Rosenstark

4
또한 구조체를 유용하게 만드는 불변성이 아니라고 덧붙일 수 있습니까 (매우 좋은 일이지만). 구조체를 변형시킬 수는 있지만 mutating어떤 함수가 상태를 변경하는지에 대해 명시 적으로 표시해야합니다 . 그러나 가치 유형 으로서의 본질 이 중요합니다. 구조체를 선언하면 let변형 함수를 호출 할 수 없습니다. 가치 유형을 통한 더 나은 프로그래밍 에 대한 WWDC 15 비디오 는 이에 대한 훌륭한 리소스입니다.
Abizern

1
감사합니다 @Abizern, 나는 당신의 의견을 읽기 전에 이것을 이해하지 못했습니다. 객체의 경우 vs. var는 큰 차이가 없지만 구조체의 경우에는 큰 차이가 있습니다. 이것을 지적 해 주셔서 감사합니다.
Dan Rosenstark

27

우리가 Struct가치 유형 이고 Class참조 유형 이라고 가정하면 .

값 유형과 참조 유형이 무엇인지 모르는 경우 참조 별 전달과 값별 전달의 차이점무엇입니까? 를 참조하십시오 .

mikeash의 게시물을 기반으로 :

... 극단적이고 명백한 예를 먼저 살펴 보겠습니다. 정수는 분명히 복사 가능합니다. 값 유형이어야합니다. 네트워크 소켓은 현명하게 복사 할 수 없습니다. 참조 유형이어야합니다. x, y 쌍과 같이 포인트를 복사 할 수 있습니다. 값 유형이어야합니다. 디스크를 나타내는 컨트롤러는 현명하게 복사 할 수 없습니다. 이것이 참조 유형이어야합니다.

일부 유형은 복사 할 수 있지만 항상 발생하지 않을 수도 있습니다. 이는 참조 유형이어야 함을 나타냅니다. 예를 들어, 화면의 버튼을 개념적으로 복사 할 수 있습니다. 사본은 원본과 동일하지 않습니다. 사본을 클릭하면 원본이 활성화되지 않습니다. 사본은 화면에서 동일한 위치를 차지하지 않습니다. 버튼을 전달하거나 새 변수에 넣으면 원래 버튼을 참조하고 명시 적으로 요청했을 때만 사본을 만들고 싶을 것입니다. 즉, 버튼 유형은 참조 유형이어야합니다.

보기 및 창 컨트롤러도 비슷한 예입니다. 아마도 복사가 가능할 수도 있지만, 당신이하고 싶은 일은 거의 없습니다. 참조 유형이어야합니다.

모델 유형은 어떻습니까? 시스템의 사용자를 나타내는 사용자 유형 또는 사용자가 수행 한 조치를 나타내는 범죄 유형이있을 수 있습니다. 이것들은 꽤 복사 가능하기 때문에 아마도 가치 유형이어야합니다. 그러나 프로그램의 한 위치에서 작성된 사용자 범죄에 대한 업데이트가 프로그램의 다른 부분에 표시되도록 할 수 있습니다. 이것은 사용자가 일종의 사용자 컨트롤러로 참조 유형을 관리해야 함을 나타냅니다. 냅니다. 예 :

struct User {}
class UserController {
    var users: [User]

    func add(user: User) { ... }
    func remove(userNamed: String) { ... }
    func ...
}

컬렉션은 흥미로운 경우입니다. 여기에는 문자열뿐만 아니라 배열 및 사전과 같은 것들이 포함됩니다. 복사 할 수 있습니까? 명백하게. 원하는 일을 쉽고 자주 복사하고 있습니까? 덜 명확합니다.

대부분의 언어는 이에 대해 "아니오"라고 말하고 컬렉션을 참조 유형으로 만듭니다. Objective-C 및 Java, Python 및 JavaScript 및 거의 모든 다른 언어에서 생각할 수 있습니다. (STL 콜렉션 유형의 C ++은 한 가지 주요 예외이지만, C ++은 모든 것을 이상하게 수행하는 언어 세계의 미치광이입니다.)

스위프트는 "예"라고 말했다. 이는 Array와 Dictionary, String과 같은 타입이 클래스가 아닌 구조체라는 것을 의미한다. 그들은 할당 및 매개 변수로 전달시 복사됩니다. 사본이 싸기 만하면 Swift가 달성하기 위해 매우 열심히 노력하는 것이 전적으로 합리적인 선택입니다. ...

나는 개인적으로 그런 수업을하지 않습니다. 나는 보통 UserController 대신 내 UserManager 를 명명 하지만 아이디어는 동일합니다.

또한 함수의 모든 인스턴스를 재정의해야 할 때 클래스를 사용하지 마십시오. 공유 기능 .

따라서 클래스의 여러 하위 클래스를 갖는 대신. 프로토콜에 맞는 여러 구조체를 사용하십시오.


구조체에 대한 또 다른 합리적인 사례는 이전 모델과 새 모델의 델타 / 차이를 원할 때입니다. 참조 유형을 사용하면 즉시 사용할 수 없습니다. 값 유형을 사용하면 돌연변이가 공유되지 않습니다.


1
내가 찾던 설명의 종류. Nice write up :)
androCoder-BD

매우 유용한 컨트롤러 예
문의하십시오.

1
@AskP 나는 마이크에게 자신에게 이메일을 보내고 그 여분의 코드를 얻었다 :)
Honey

18

몇 가지 장점 :

  • 공유 할 수 없어서 자동으로 스레드 세이프
  • isa 및 refcount가 없기 때문에 적은 메모리를 사용합니다 (실제로 스택이 일반적으로 할당 됨)
  • 메소드는 항상 정적으로 전달되므로 인라인 될 수 있습니다 (@final은 클래스에 대해이 작업을 수행 할 수 있지만)
  • 스레드 안전성과 같은 이유로 추론하기 쉬움

그것이이 답변의 범위를 벗어 났는지 확실하지 않지만 "메소드가 항상 정적으로 전달됩니다"지점을 설명 할 수 있습니까?
Dan Rosenstark 1

2
확실한. 또한주의 사항을 첨부 할 수도 있습니다. 동적 디스패치의 목적은 어떤 것을 사용해야할지 모르는 경우 구현을 선택하는 것입니다. Swift에서는 상속 (하위 클래스에서 재정의 될 수 있음) 또는 일반 함수 (일반 매개 변수가 무엇인지 알 수 없음) 때문일 수 있습니다. Structs는 상속 될 수 없으며 전체 모듈 최적화 + 제네릭 전문화는 알려지지 않은 제네릭을 대부분 제거하므로 호출 할 항목을 찾아 보지 않고 직접 메서드를 호출 할 수 있습니다. 특수화 된 제네릭은 여전히 ​​구조체에 대해 동적 디스패치를 ​​수행합니다.
Catfish_Man

1
고마워, 좋은 설명. 따라서 우리는 런타임 속도가 더 높거나 IDE 관점에서 모호성이 더 낮거나 두 가지 모두를 기대하고 있습니까?
Dan Rosenstark

1
대부분 전자입니다.
Catfish_Man

프로토콜을 통해 구조체를 참조하면 메서드가 정적으로 전달되지 않습니다.
Cristik

12

구조는 클래스보다 훨씬 빠릅니다. 또한 상속이 필요한 경우 클래스를 사용해야합니다. 가장 중요한 점은 클래스는 참조 유형이고 구조는 값 유형입니다. 예를 들어

class Flight {
    var id:Int?
    var description:String?
    var destination:String?
    var airlines:String?
    init(){
        id = 100
        description = "first ever flight of Virgin Airlines"
        destination = "london"
        airlines = "Virgin Airlines"
    } 
}

struct Flight2 {
    var id:Int
    var description:String
    var destination:String
    var airlines:String  
}

이제 둘 다의 인스턴스를 만들 수 있습니다.

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

이제이 인스턴스를 id, description, destination 등을 수정하는 두 함수에 전달할 수 있습니다.

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

또한,

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

그래서,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

이제 flightA의 ID와 설명을 인쇄하면

id = 200
description = "second flight of Virgin Airlines"

여기서, modify 메소드에 전달 된 매개 변수가 실제로 flightA 객체 (참조 유형)의 메모리 주소를 가리 키기 때문에 FlightA의 id와 설명이 변경되었음을 알 수 있습니다.

이제 FLightB 인스턴스의 ID와 설명을 인쇄하면

id = 100
description = "first ever flight of Virgin Airlines"

여기서는 modifyFlight2 메소드에서 Flight2의 실제 인스턴스가 참조 (값 유형)가 아닌 전달이기 때문에 FlightB 인스턴스가 변경되지 않았 음을 알 수 있습니다.


2
FLightB의 인스턴스를 만들지 않았습니다
David Seek

1
그렇다면 왜 FlightB bro에 대해 이야기하고 있습니까? Here we can see that the FlightB instance is not changed
David Seek

@ManojKarki, 좋은 대답입니다. FlightA를 선언하고 FlightB를 선언한다고 생각할 때 flightA를 두 번 선언했다고 지적하고 싶었습니다.
ScottyBlades 1

11

Structs있습니다 value typeClasses입니다reference type

  • 값 유형이 참조 유형보다 빠릅니다.
  • 여러 스레드가 경쟁 조건이나 교착 상태에 대해 걱정할 필요없이 인스턴스를 변경할 수 있으므로 값 유형 인스턴스는 다중 스레드 환경에서 안전합니다.
  • 값 유형에는 참조 유형과 달리 참조가 없습니다. 따라서 메모리 누수가 없습니다.

다음과 value같은 경우 유형을 사용하십시오 .

  • 사본이 독립적 인 상태를 갖기를 원하면 데이터가 여러 스레드에서 코드로 사용됩니다.

다음과 reference같은 경우 유형을 사용하십시오 .

  • 변경 가능 공유 상태를 작성하려고합니다.

추가 정보는 Apple 문서에서도 찾을 수 있습니다

https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html


추가 정보

신속한 가치 유형은 스택에 유지됩니다. 프로세스에서 각 스레드에는 고유 한 스택 공간이 있으므로 다른 스레드는 값 유형에 직접 액세스 할 수 없습니다. 따라서 경쟁 조건, 잠금, 교착 상태 또는 관련 스레드 동기화 복잡성이 없습니다.

값 유형은 동적 메모리 할당 또는 참조 카운팅이 필요하지 않으며 둘 다 비싼 작업입니다. 동시에 값 형식의 메서드는 정적으로 전달됩니다. 이는 성능 측면에서 가치 유형에 유리하게 큰 이점을 제공합니다.

여기에 스위프트 목록이 있습니다.

값 유형 :

  • 구조
  • 열거 형
  • 튜플
  • 프리미티브 (Int, Double, Bool 등)
  • 컬렉션 (배열, 문자열, 사전, 세트)

참조 유형 :

  • 수업
  • NSObject에서 오는 것
  • 함수
  • 폐쇄

5

이 Apple 블로그 게시물 에서 값 유형과 참조 유형의 관점에서 질문에 대답하면 매우 간단하게 나타납니다.

다음과 같은 경우에 값 유형 [예 : struct, enum]을 사용하십시오.

  • ==로 인스턴스 데이터를 비교하는 것이 합리적입니다.
  • 사본이 독립 상태를 갖기를 원합니다
  • 데이터는 여러 스레드에서 코드로 사용됩니다

다음과 같은 경우 참조 유형 [예 : 클래스]을 사용하십시오.

  • ===와 인스턴스 ID를 비교하는 것이 합리적입니다.
  • 공유 가능한 변경 가능한 상태를 만들려고합니다.

이 기사에서 언급했듯이 쓰기 가능한 속성이없는 클래스는 구조체와 동일하게 작동하지만 한 가지주의 사항이 있습니다. 구조는 스레드 안전 모델에 가장 적합 합니다 .


3

클래스를 사용하면 상속을 받고 참조로 전달되며, 구조체에는 상속이 없으며 값으로 전달됩니다.

Swift에는 훌륭한 WWDC 세션이 있으며이 특정 질문은 그 중 하나에서 자세히 설명됩니다. 언어 가이드 나 iBook보다 훨씬 빠르게 속도를 높일 수 있으므로 시청하십시오.


언급 한 내용과 관련이있는 링크를 제공해 주시겠습니까? WWDC에 원인 내가 하나가이 특정 주제에 대해 이야기보고 싶습니다 선택할 꽤있다
MMachinegun

나를 위해 이것은 여기에서 좋은 시작입니다 : github.com/raywenderlich/…
MMachinegun

2
그는 아마도 Swift의 Protocol-Oriented Programming이라는 훌륭한 세션에 대해 이야기하고있을 것입니다. (Links : video , transcript )
zekel

2

구조체가 기능을 덜 제공한다고 말하고 싶지는 않습니다.

물론, 돌연변이 기능을 제외하고는 자기 자신은 변하지 않지만 그게 전부입니다.

상속은 모든 클래스가 추상적이거나 최종적이어야한다는 오래된 아이디어를 고수하는 한 잘 작동합니다.

프로토콜로 추상 클래스를 구현하고 구조체로 최종 클래스를 구현하십시오.

structs의 좋은 점은 copy on write가 처리하기 때문에 공유 가능한 변경 가능한 상태를 만들지 않고 필드를 변경할 수 있다는 것입니다. :)

그렇기 때문에 다음 예제의 속성 / 필드가 모두 변경 가능한 이유는 Java 또는 C # 또는 빠른 클래스 에서는 불가능 합니다.

"example"이라는 함수의 맨 아래에 약간 더럽고 간단한 사용법이있는 상속 구조 예제 :

protocol EventVisitor
{
    func visit(event: TimeEvent)
    func visit(event: StatusEvent)
}

protocol Event
{
    var ts: Int64 { get set }

    func accept(visitor: EventVisitor)
}

struct TimeEvent : Event
{
    var ts: Int64
    var time: Int64

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }
}

protocol StatusEventVisitor
{
    func visit(event: StatusLostStatusEvent)
    func visit(event: StatusChangedStatusEvent)
}

protocol StatusEvent : Event
{
    var deviceId: Int64 { get set }

    func accept(visitor: StatusEventVisitor)
}

struct StatusLostStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var reason: String

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

struct StatusChangedStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var newStatus: UInt32
    var oldStatus: UInt32

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

func readEvent(fd: Int) -> Event
{
    return TimeEvent(ts: 123, time: 56789)
}

func example()
{
    class Visitor : EventVisitor
    {
        var status: UInt32 = 3;

        func visit(event: TimeEvent)
        {
            print("A time event: \(event)")
        }

        func visit(event: StatusEvent)
        {
            print("A status event: \(event)")

            if let change = event as? StatusChangedStatusEvent
            {
                status = change.newStatus
            }
        }
    }

    let visitor = Visitor()

    readEvent(1).accept(visitor)

    print("status: \(visitor.status)")
}

2

Swift에서는 프로토콜 지향 프로그래밍이라는 새로운 프로그래밍 패턴이 도입되었습니다.

창조 패턴 :

신속하게 Struct는 가치 유형입니다. 자동으로 복제되는 입니다. 따라서 프로토 타입 패턴을 무료로 구현하는 데 필요한 동작을 얻습니다.

반면 클래스를 자동으로 할당하는 동안 복제되지 않은 참조 형이다. 프로토 타입 패턴을 구현하려면 클래스가 NSCopying프로토콜을 채택해야 합니다.


얕은 사본 은 참조 만 복제하고, 해당 오브젝트를 가리키는 반면, 깊은 사본은 오브젝트의 참조를 복제합니다.


참조 유형 에 대해 딥 카피 를 구현 하는 것은 지루한 작업이되었습니다. 클래스에 추가 참조 유형이 포함 된 경우 각 참조 특성에 대해 프로토 타입 패턴을 구현해야합니다. 그런 다음 프로토콜 을 구현하여 전체 객체 그래프를 실제로 복사해야 합니다.NSCopying

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

structs와 enums 를 사용 하면 복사 로직을 구현할 필요가 없으므로 코드가 더 단순 해졌습니다.


1

많은 Cocoa API에는 NSObject 서브 클래스가 필요하므로 클래스를 사용해야합니다. 그러나 그 외에 Apple의 Swift 블로그에서 다음 사례를 사용하여 struct / enum 값 유형을 사용할지 클래스 참조 유형을 사용할지 결정할 수 있습니다.

https://developer.apple.com/swift/blog/?id=10


0

이 답변에주의를 기울이지 않는 한 가지 점은 클래스 대 구조체를 보유하는 변수는 let여전히 객체의 속성을 변경할 수 있지만 구조체로는이를 수행 할 수는 없다는 것입니다.

이것은 변수가 다른 객체를 가리 키지 않고 객체를 수정해야하는 경우에 유용합니다. 예를 들어, 인스턴스 변수가 많은 경우 서로 업데이트해야합니다. 구조체 인 경우 varSwift의 상수 값 유형은 제로 돌연변이를 올바르게 허용하지만 참조 유형 (클래스)은 이러한 방식으로 작동하지 않으므로 변수를 사용하여 다른 객체로 변수를 재설정 해야합니다.


0

구조체는 값 유형이므로 스택에 저장하는 메모리를 매우 쉽게 만들 수 있습니다. 스트럭처는 쉽게 액세스 할 수 있으며 작업 범위 후에 스택 상단에서 팝을 통해 스택 메모리에서 쉽게 할당 해제됩니다. 반면에 클래스는 힙에 저장하는 참조 유형이며 한 클래스 객체의 변경 사항은 단단히 결합되고 참조 유형이므로 다른 객체에 영향을 미칩니다. 구조의 모든 멤버는 공용이지만 클래스의 모든 멤버는 비공개입니다 .

구조체의 단점은 상속 할 수 없다는 것입니다.


-7
  • 구조와 클래스는 사용자 정의 데이터 형식입니다

  • 기본적으로 구조는 공용이지만 클래스는 비공개입니다

  • 클래스는 캡슐화의 원리를 구현

  • 클래스의 객체는 힙 메모리에 생성됩니다

  • 클래스는 재사용 성을 위해 사용되는 반면, 구조는 동일한 구조로 데이터를 그룹화하는 데 사용됩니다.

  • 구조 데이터 멤버는 직접 초기화 할 수 없지만 구조 외부에서 할당 할 수 있습니다.

  • 클래스 데이터 멤버는 덜 매개 변수 생성자에 의해 직접 초기화되고 매개 변수화 된 생성자에 의해 지정 될 수 있습니다


2
최악의 답변!
J. Doe

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