[객체지향 프로그래밍과 swift] 구조체와 클래스

silverCastle·2021년 8월 27일
0
post-thumbnail

구조체와 클래스는 프로그래머가 데이터를 용도에 맞게 묶어서 표현하고자 할 때 유용한데, 이 둘의 큰 차이점은 구조체의 인스턴스는 값 타입이고 클래스의 인스턴스는 참조 타입이라는 것이다.

💡 구조체

구조체는 struct 키워드로 정의한다. 구조체를 정의한다는 것은 새로운 타입을 생성해주는 것과 마찬가지이므로 Int, Float, String과 같이 대문자 카멜케이스를 사용하여 이름을 지어준다.

struct Student {
    var name: String
    var age: Int
}

구조체를 정의했으면 이 구조체의 인스턴스를 생성해야 할텐데, 구조체에 기본적으로 생성되는 멤버 와이즈 이니셜라이저를 사용하여 인스턴스를 생성하고 초기화할 수 있다.

var myInfo: Student = Student(name: "silverCastle", age: 24)
print("제 이름은 \(myInfo.name)입니다.")
print("제 나이는 \(myInfo.age)입니다.")

결과

제 이름은 silverCastle입니다.
제 나이는 24입니다.

만약, 구조체를 let으로 선언한다면?
구조체의 인스턴스는 값 타입이므로 인스턴스 내부의 프로퍼티 값을 변경할 수 없음을 명심하자.

let yourInfo: Student = Student(name: "bronzeCastle", age: 25)
yourInfo.name = "goldCastle"

결과

Cannot assign to property: 'yourInfo' is a 'let' constant

💡 클래스

클래스는 class 키워드로 정의한다. 클래스도 구조체와 마찬가지로 대문자 카멜케이스를 사용하여 이름을 지어준다.
클래스를 정의하는 방법은 구조체와 흡사하지만 클래스는 상속받을 수 있기 때문에 상속 받을 경우에는 클래스 이름 뒤에 콜론(:)을 써주고 부모클래스를 명시한다.
만약, 클래스를 let으로 선언한다면?
클래스의 인스턴스는 참조 타입이므로 인스턴스 내부의 프로퍼티 값을 변경할 수 있음을 명심하자.

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

var me: Person = Person()
me.height = 175
me.weight = 74
print("내 키는 \(me.height)입니다.")
print("내 몸무게는 \(me.weight)입니다.")

let you: Person = Person()
you.height = 156
you.weight = 50
print("당신의 키는 \(you.height)입니다.")
print("당신의 몸무게는 \(you.weight)입니다.")

결과

내 키는 175.0입니다.
내 몸무게는 74.0입니다.
당신의 키는 156.0입니다.
당신의 몸무게는 50.0입니다.

또한, 클래스의 인스턴스는 더는 참조할 필요가 없을 때 메모리에서 해제되는데 이 과정을 소멸이라고 한다. 소멸되기 직전 deinit라는 메서드가 호출된다. deinit 메서드는 클래스당 하나만 구현할 수 있으며, 매개변수와 반환 값을 가질 수 없는 게 특징이다.

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0

    deinit {
        print("Person 클래스의 인스턴스가 소멸됩니다.")
    }
}
var me: Person? = Person()
me = nil

결과

Person 클래스의 인스턴스가 소멸됩니다.

💡 구조체와 클래스의 차이

공통점

  • 값을 저장하기 위해 프로퍼티를 정의할 수 있다.
  • 기능 실행을 위해 메서드를 정의할 수 있다.
  • 서브스크립트 문법을 통해 구조체 또는 클래스가 갖는 값에 접근하도록 서브스크립트를 정의할 수 있다.
  • 초기화될 때의 상태를 지정하기 위해 이니셜라이저를 정의할 수 있다.
  • 초기구현과 더불어 새로운 기능 추가를 위해 익스텐션을 통해 확장할 수 있다.
  • 특정 기능을 실행하기 위해 특정 프로토콜을 준수할 수 있다.

차이점

  • 구조체는 상속할 수 없다.
  • 타입캐스팅은 클래스의 인스턴스에만 허용된다.
  • 디이니셜라이저는 클래스의 인스턴스에만 활용할 수 있다.
  • 클래스의 인스턴스는 참조 타입이므로 참조 횟수 계산은 클래스의 인스턴스에만 적용된다.

앞서 말했다시피, 가장 큰 차이점은 구조체의 인스턴스는 값 타입이고 클래스의 인스턴스는 참조 타입이라는 것인데 예를 들어, 어떤 함수의 전달인자로 값 타입의 값을 넘긴다면 전달될 값이 복사되어 전달되지만 전달인자로 참조 타입이 전달된다면 값을 복사하지 않고 참조가 전달된다. 여기서 참조는 주소라고 생각하면 된다.

구조체

struct Student {
    var name: String
    var age: Int
}

var myInfo: Student = Student(name: "silverCastle", age: 24)
myInfo.age = 100

var yourInfo: Student = myInfo

print("내 나이는 \(myInfo.age)입니다.")
print("당신의 나이는 \(yourInfo.age)입니다.")

yourInfo.age = 200
print("내 나이는 \(myInfo.age)입니다.")
print("당신의 나이는 \(yourInfo.age)입니다.")

결과

내 나이는 100입니다.
당신의 나이는 100입니다.
내 나이는 100입니다.
당신의 나이는 200입니다.

클래스

class Person {
    var height: Float = 0.0
    var weight: Float = 0.0
}

var me: Person = Person()
me.height = 175
me.weight = 74

let you: Person = me

print("내 키는 \(me.height)입니다.")
print("당신의 키는 \(you.height)입니다.")

you.height = 156

print("내 키는 \(me.height)입니다.")
print("당신의 키는 \(you.height)입니다.")

결과

내 키는 175.0입니다.
당신의 키는 175.0입니다.
내 키는 156.0입니다.
당신의 키는 156.0입니다.

이제 구조체와 클래스의 공통점과 차이점을 알게 되었다. 그렇다면 언제 구조체를 쓰고 언제 클래스를 써야할까?
애플은 가이드라인에서 다음 조건 중 하나라도 해당된다면 구조체를 사용하라고 권장하고 있다.

  • 연관된 간단한 값의 집합을 캡슐화하는 것만이 목적일 때
  • 캡슐화한 값을 참조하는 것보다 복사하는 것이 합당할 때
  • 구조체에 저장된 프로퍼티가 값 타입이며 참조하는 것보다 복사하는 것이 합당할 때
  • 다른 타입으로부터 상속받거나 자신을 상속할 필요가 없을 때

0개의 댓글