호댕의 iOS 개발

[Swift] Class와 Structure의 차이는 무엇일까? 본문

Software Engineering/Swift

[Swift] Class와 Structure의 차이는 무엇일까?

호르댕댕댕 2021. 10. 16. 14:02

해당 글은 [Swift 공식문서 - Structures and Classes]를 읽고 작성한 내용입니다. 

 

Class와 Structure는 코드의 building block이 되는 유연한 범용 구조이다. 

상수, 변수, 함수를 선언하는 것과 동일한 방법으로 구조체와 클래스를 정의할 수 있다. 그렇다면 Class와 Structure의 공통점과 차이점은 무엇일까?

 

Class와 Structure의 공통점 

  • 값을 저장하는 프로퍼티를 정의할 수 있다.
  • 기능을 제공하는 메소드를 정의할 수 있다.
  • Subscript 구문을 사용해서 값에 접근할 수 있도록 Subscript를 정의할 수 있다.
  • Initializer를 정의할 수 있다.
  • 기본 기능 이상으로 기능을 확장할 수 있다.
  • 특정 종류의 기능을 제공하는 프로토콜을 준수한다.

 

Class에만 있는 기능

  • 상속을 통해 클래스에 다른 클래스의 특성을 상속할 수 있다.
  • 타입 캐스팅을 통해 런타임에 클래스 인스턴스의 유형을 확인하고 해석할 수 있다.
  • Deinitializer를 활용하면 클래스의 인스턴스에 할당된 리소스를 해제할 수 있다.
  • Reference Counting은 하나 이상의 클래스 인스턴스에 대한 참조를 허용한다.

 

클래스에 있는 추가적인 기능으로 사용 시 구조체에 비해 더욱 복잡해진다. 

일반적으로 사용하기 쉽기 때문에 구조체가 선호되며, 클래스의 경우 필요하거나, 사용에 적절한 경우 사용하게 된다. 

(실제로 Swift의 많은 데이터 타입들이 구조체와 열거형으로 되어있다)

 

 

값타입과 참조타입

Class와 Stucture의 차이점 중 하나가 Class는 참조타입이고 Structure는 값타입이라는 것이다. 

 

그렇다면 먼저 값타입에 대해 알아보자 

 

Structures and Enumerations Are Value Types

제목에서 나와있듯 구조체와 열거형은 값타입(Value Type) 이다. 

그렇다면 값타입이란 무엇일까?

 

값타입이란 "변수나 상수에 할당될 때 함수에 전달되는 값이 복사"되는 것이다. 

Swift의 기본타입(Int, Float, Bool, String, Array, Dictionary 등)이 값타입으로 되어 있다. 

 

또한 Swift의 모든 구조체와 열거형도 '값타입'으로 되어 있다.

 

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

cinema.width = 2048

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"

출처: [Swift 공식문서 - Structures and Classes]

 

Structures and Classes — The Swift Programming Language (Swift 5.5)

Structures and Classes Structures and classes are general-purpose, flexible constructs that become the building blocks of your program’s code. You define properties and methods to add functionality to your structures and classes using the same syntax you

docs.swift.org

위 코드를 보면 `hd`라는 상수가 선언되어 있고, 거기에 Resolution이라는 인스턴스를 만들었다. 

여기서 변수 `cinema`를 선언하고 거기에 `hd`를 할당해주면 기존 인스턴스의 복사본이 존재하고 새로운 복사본이 `cinema`에 할당되게 된다.

 

비록 `hd`와 `cinema` 같은 넓이와 높이를 가지고 있지만 완전히 다른 인스턴스인 것이다. 

 

따라서 `cinema`에 width를 2048로 수정한다면 cinema의 width값은 변하지만 기존의 hd는 이전과 동일하게 width가 1920으로 유지되고 있다. 

 

이는 열거형에서도 동일하다. 

 

enum CompassPoint {
    case north, south, east, west
    mutating func turnNorth() {
        self = .north
    }
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()

print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"

원래 currentDirection은 서쪽으로 되어 있었고 rememberedDirection에 currentDirection을 할당했다. 이후 currentDirection을 북쪽으로 바꿔도 rememberedDirection은 여전히 서쪽으로 남아있다.

 

 

Classes Are Reference Types

값 타입과 다르게 참조 타입의 경우 변수나 상수에 할당되거나, 함수에 전달될 때 복사를 하지 않는다. 복사 대신 기존 인스턴스에 대한 참조가 사용된다.

 

예시를 살펴보자

class VideoMode {
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

VideoMode라는 클래스를 tenEighty라는 새로운 상수 인스턴스에 할당했다. 

 

또 여기서 tenEighty라는 인스턴스를 alsoTenEighty라는 새로운 인스턴스에 할당하게 된다면

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

VideoMode에 있는 frameRate까지 전부 변하는 것이다. 즉, 현재 frameRate는 30.0으로 VideoMode, tenEighty, alsoTenEighty에 변경되어 있는 것이다. 

 

클래스는 참조타입이기 때문에 여러 상수와 변수가 나중에 클래스의 동일한 단일 인스턴스를 참조할 수 있다. 구조체와 열거형의 경우 값을 복사해오기 때문에 클래스와는 다르다.

두 개의 상수 또는 변수가 클래스의 동일한 인스턴스를 참조했는지 확인하려면 아래 연산자를 사용하면 된다.

  • 같다 === → == 연산자와는 완전히 다르다.
  • 다르다 !==
if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
Comments