[iOS] 구조체(Struct)와 클래스(Class)

강치우·2024년 4월 1일
0

수입푸드

목록 보기
9/13
post-thumbnail
post-custom-banner

안녕하세요 !

오늘은 구조체와 클래스의 차이점을 알아볼려고 합니다 :)

서로 닮은듯, 다른 친구들인데 한번 오늘 구조체와 클래스의 공통점과 차이점을 알아보는 시간을 가져볼게요

공통점

  • 프로퍼티 정의가 가능하다.
  • 메소드 정의가 가능하다.
  • initializer 정의가 가능하다.
  • extension이 가능하다.
  • protocol이 가능하다.

차이점

  • 구조체는 value type, 클래스는 reference type
  • 구조체는 상속이 불가능
  • 구조체에서는 AnyObject로 타입캐스팅이 불가능
  • 구조체는 생성자를 구현하지 않을 시 기본 initializer를 사용할 수 있다.
  • 클래스는 reference counting으로 메모리 관리가 가능하다.

이러한 공통점과 차이점이 있습니다.

자 여기서 value type, reference type이 뭐냐?


값 타입(Value Type)

값 타입은 데이터를 전달할 때 그 값의 복사본을 생성하여 전달합니다. 즉, 원본과 복사본은 완전히 독립적인 개체가 됩니다.

Swift에서는 구조체(Struct), 열거형(Enum), 기본 데이터 타입(Int, String, Bool 등)이 값 타입입니다.

값 타입의 주요 특징은 데이터의 안전성이 높다는 점입니다.

데이터를 전달할 때마다 복사본을 사용하기 때문에 원본 데이터를 실수로 변경할 위험이 적습니다.


struct Point {
    var x: Int
    var y: Int
}

var originalPoint = Point(x: 10, y: 20)
var copiedPoint = originalPoint
copiedPoint.x = 30

originalPoint와 copiedPoint는 완전히 다른 독립적인 개체이기 때문에,

copiedPoint를 변경해도 originalPoint에 영향을 미치지 않습니다.




참조 타입(Reference Type)

참조 타입은 데이터를 전달할 때 값의 복사본을 전달하는 것이 아니라 데이터가 저장된 메모리 위치(참조)를 전달합니다.

클래스(Class)가 참조 타입의 대표적인 예입니다.

참조 타입의 경우, 두 변수가 같은 메모리 위치를 참조할 수 있으므로, 한 변수를 통한 데이터의 변경이 다른 변수에도 영향을 미칩니다.


class Person {
    var name: String

    init(name: String) {
        self.name = name
    }
}

var originalPerson = Person(name: "홍길동")
var referencedPerson = originalPerson
referencedPerson.name = "임꺽정"

참조 타입의 특징은 인스턴스를 변수나 상수에 할당하거나 함수에 전달할 때,

실제 데이터의 복사본을 만드는 것이 아니라 해당 데이터를 가리키는 참조(메모리 주소)를 전달하기 때문에

referencedPerson을 변경하면 originalPerson에도 영향을 미칩니다.

따라서 referencedPerson.name을 "임꺽정"으로 변경하면, referencedPerson이 가리키는 메모리 위치에 있는 name 프로퍼티가 변경됩니다


값타입과 참조타입을 알아봤으니, 다시 본론으로 돌아가서

StructClass의 차이점을 예시코드로 보여드릴게요


Struct

struct UserData {
    var name: String
    var age: Int
    mutating func updateUser(_ name: String, _ age: Int) {
        self.name = name
        self.age = age
        
        print("이름 : \(name), 나이 : \(age)")
    }
}
var shark = UserData(name: "상어", age: 16)
print("이름 : \(shark.name), 나이 : \(shark.age)")
// 이름 : 상어, 나이 : 16
var hanabi = shark
hanabi.name = "하나비"
hanabi.age = 27
print("이름 : \(shark.name), 나이 : \(shark.age)")
// 이름 : 상어, 나이 : 16
print("이름 : \(hanabi.name), 나이 : \(hanabi.age)")
// 이름 : 하나비, 나이 : 27
shark.updateUser("천랑성", 20)
// 이름 : 천랑성, 나이 : 20

struct의 핵심은 값 복사입니다.

그래서 hanabi에 shark의 값을 복사하고 hanabi의 속성을 변경해도 shark의 속성은 변경되지 않습니다.

하지만,

값 복사이면서 copy-on-write이기 때문에 대입할 때 복사가 일어나는 것이 아닌,

수정이 발생할 때 값이 복사됩니다.

그렇기 때문에


hanabi.name = "하나비"

이 부분에서 실제로 복사가 일어나는 것을 볼 수 있습니다.


아 그리고, struct는 메서드 앞에 mutating을 꼭 해줘야 하는데요,

왜냐면 구조체는 값타입(value type)이기 때문에 메서드 안에서 프로퍼티 변경이 불가능하답니다.

그래서 mutating을 붙이면 해당 객체가 다시 생성되면서 변경이 가능하게 됩니다.


Class

class Phone {
    var name: String
    var color: String
    
    // struct와 달리 class는 이니셜라이즈를 지정해야합니다.
    init(name: String, color: String) {
        self.name = name
        self.color = color
    }
    
    func updatePhone(name: String, color: String) {
        self.name = name
        self.color = color
        
        print("폰 : \(name), 색상 : \(color)")
    }
}
let iPhone8 = Phone(name: "iPhone8", color: "red")
print("폰 : \(iPhone8.name), 색상 : \(iPhone8.color)")
// 폰 : iPhone8, 색상 : red
var iPhoneXs = iPhone8
iPhoneXs.name = "iPhoneXs"
iPhoneXs.color = "grey"
print("폰 : \(iPhone8.name), 색상 : \(iPhone8.color)")
// 폰 : iPhoneXs, 색상 : grey
print("폰 : \(iPhoneXs.name), 색상 : \(iPhoneXs.color)")
// 폰 : iPhoneXs, 색상 : grey
iPhoneXs.updatePhone(name: "iPhoneX", color: "black")
// 폰 : iPhoneX, 색상 : black

class는 reference type이기 때문에 iPhoneXs를 iPhone8을 대입하고 iPhoneXs의 속성을 변형하면 iPhone8에 대한 속성도 함께 변형이 된답니다.

뿐만 아니라, class는 메서드 안에서 프로퍼티 변경이 가능하기 때문에 mutating을 붙이지 않아도 됩니다


그래서 어떤 경우에 사용해야하는데?

구조체(Struct)를 사용하는 경우

독립성이 중요할 때

구조체의 인스턴스는 값 타입이므로, 복사될 때마다 새로운 인스턴스가 생성되니 공유 상태를 피하고 싶을 때 구조체를 사용합니다.

데이터 모델링이 간단할 때

작은 데이터 모델이나 간단한 값의 집합을 다룰 때 구조체를 사용하는 것이 좋습니다.

상속이 필요 없을 때

구조체는 상속을 지원하지 않기에, 상속이 필요 없는 경우에 구조체를 사용하는 것이 적합합니다.


클래스(Class)를 사용하는 경우

인스턴스 공유가 필요할 때

여러 변수나 함수가 동일한 인스턴스를 참조해야 할 필요가 있을 때 클래스를 사용합니다.

상속을 사용해야 할 때

기능 확장이나 코드 재사용성을 위해 상속을 사용하고 싶을 때 클래스를 사용합니다.


마치며

오늘은 구조체와 클래스의 차이점을 알아봤는데요~

구조체와 클래스 사이의 선택은 구현하려는 기능, 성능 요구 사항, 메모리 관리 방식 등 여러 요소를 고려해야 합니다.

Swift는 구조체 사용을 권장하지만, 앱의 특정 요구 사항에 따라 클래스의 사용이 불가피할 수 있습니다.

따라서, 상황에 따라 가장 적합한 타입을 선택하는 것이 중요하겠죠?

아직 저도 일일이 고려하면서 사용하는 수준은 아니긴 하지만.. 언젠간 고려하면서 사용하는 날이 오겠죠..?

그럼 안뇽 :)


참고한 블로그

profile
자허블을 좀 더 좋아하긴 합니다.
post-custom-banner

0개의 댓글