[Swift] 클래스와 구조체

안녕하세요~ 차니에요!

오늘은 스위프트의 클래스와 구조체에 대해 알아볼게요.

 

1. 클래스와 구조체

클래스와 구조체는 프로그램의 코드를 조직화 하기 위해 일반적으로 사용하며 OOP를 위한 필수 요소입니다.

 

1-1. 기본 문법

class SomeClass {
    // 클래스 내용은 여기에
}
struct SomeStructure {
    // 구조체 내용은 여기에
}

위 코드는 클래스와 구조체를 생성하는 기본 문법이며 테스트를 위한 클래스와 구조체를 하나씩 생성하겠습니다.

 

struct BodySize {
    var height = 0.0
    var weight = 0.0
}

class Person {
    var name = ""
    var age = 0
    var body = BodySize()
    
    func calcBMI() -> String { String(format: "%.2f", body.weight / (pow(2.0, (body.height / 100)))) }
}

 

1-2. 클래스와 구조체 인스턴스

var bodySize = BodySize() // 구조체 인스턴스 생성
var person = Person() // 클래스 인스턴스 생성

 

클래스와 구조체 이름 뒤에 빈 괄호를 적으면 생성됩니다.

필요에 따라 괄호 안에 value 값을 초기화 해주어도 됩니다.

 

print(bodySize.weight) // 0.0
bodySize.weight = 75.0
print(bodySize.weight) // 75.0

인스턴스 생성 이후 점(dot) 문법을 통해 프로퍼티 값을 가져올 수도, 값을 할당할 수도 있습니다.

 

var bodySize = BodySize(height: 165, weight: 51)
var nana = Person()
nana.name = "nana"
nana.age = 23
nana.body = bodySize
print("\(nana.name)의 BMI는 \(nana.calcBMI())입니다.")

이를 활용하면 위와 같은 방식으로 사용할 수도 있습니다.

 

 

2. 클래스와 구조체 비교

위 예제에서 본 것 처럼 프로퍼티를 저장한다던가 인스턴스를 생성해서 사용한다던가 메소드를 정의한다던가 클래스와 구조체는 공통점이 많습니다. 그럼 차이점은 어떤 게 있을까? 해서 정리해보았습니다.

 

구조체는 안 되는데 클래스는 가능한 것들

 

  • 상속 (Inheritance) : 클래스의 여러 속성을 다른 클래스에 물려 줌
  • 타입 캐스팅 (Type casting) : 런타임에 클래스 인스턴스의 타입을 확인
  • 소멸자 (Deinitializers) : 할당된 자원을 해제(free up) 시킴
  • 참조 카운트 (Reference counting) : 클래스 인스턴스에 하나 이상의 참조가 가능

 

 

3. 클래스와 구조체 선택

그렇다면 이 비슷한 놈들을 어떻게 구분해서 사용해야 할까?

근본적으로 클래스는 참조 타입(call by reference)이고 구조체는 값 타입(call by value)라는 점을 기억하셔야 합니다.

 

그게 무슨 소리에요? 라는 생각이 들 수도 있을 것 같아 예제를 준비해봤습니다.

 

3-1. 구조체

struct BodySize {
    var height = 0.0
    var weight = 0.0
}

var bodySize = BodySize() // (1) BodySize 구조체 인스턴스 생성
bodySize.height = 165 // (2)
print(bodySize.height) // 165

var newBodySize = bodySize // (3)
bodySize.height = 170 (4)
print(newBodySize.height) // 165

위에서 만든 BodySize 구조체가 있습니다.

 

(1) bodySize 변수에 BodySize 인스턴스를 생성하여 할당하였습니다.

(2) bodySize.height를 165를 할당하였습니다.

(3) newBodySize 변수에 bodySize 변수(인스턴스)를 할당해주었습니다.

(4) bodySize.height를 170으로 할당하였습니다.

 

3번 과정에서 newBodySize 변수에 bodySize 변수를 할당할 때에 값이 복사(call by value)되었기 때문에 이후에 원본 인스턴스인 bodySize.height를 변경해도 newBodySize.height는 변경되지 않는 것입니다.

 

3-2. 클래스

class BodySize {
    var height = 0.0
    var weight = 0.0
}

var bodySize = BodySize() // (1) BodySize 클래스 인스턴스 생성
bodySize.height = 165 (2)
print(bodySize.height) // 165

var newBodySize = bodySize // (3)
bodySize.height = 170 // (4)
print(newBodySize.height) // 170

3-1의 구조체가 클래스로 변경된 것 말고는 동일한 코드입니다.

 

이 경우에는 bodySize.height 값을 변경하면 newBodySize.height 값도 변경되는 모습을 볼 수 있는데 구조체와 달리 클래스는 값을 참조(call by reference)하고 있기 때문입니다.

반대로 newBodySize.height를 수정하면 bodySize.height 또한 변경된 모습을 볼 수 있습니다.

 

if bodySize === newBodySize {
    print("Equal instance")
}

식별 연산자(===, !==)를 통해 같은 인스턴스를 참고하고 있는지 체크가 가능합니다.

 

3-3. 결론

일반적으로 다음의 조건 중 1개 이상을 만족하면 구조체를 사용하는 것을 고려해 볼 수 있습니다.

  • 구조체의 주 목적이 관계된 간단한 값을 캡슐화(encapsulate) 하기 위한 것인 경우
  • 구조체의 인스턴스가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체에 의해 저장된 어떠한 프로퍼티가 참조되기 보다 복사되기를 기대하는 경우
  • 구조체가 프로퍼티나 메소드 등을 상속할 필요가 없는 경우

 

이상으로 포스팅 마치겠습니다.

틀린 부분이나 더 궁금한 사항은 댓글로 남겨주세요!