반응형
LinkedIn 개발자로 성장하면서 남긴 발자취들을 확인하실 수 있습니다.
Github WWDC Student Challenge 및 Cherish, Tiramisul 등 개발한 앱들의 코드를 확인하실 수 있습니다.
개인 앱 : Cherish 내 마음을 들여다보는 시간, 체리시는 디자이너와 PM과 함께 진행 중인 1인 개발 프로젝트입니다.
10년 후, 20년 후 나는 어떤 스토리 텔러가 되어 있을지 궁금하다. 내가 만약에 아직 조금 더 탐구하고 싶은 게 있고, 궁금한 게 있다면, 그게 설사 지금 당장의 내 인생에 도움이 안 되는 것 같더라도 경험해보자. 그 경험들을 온전히 즐기며 내 것으로 만들고, 내 일에 녹여내고... 그러다보면 그 점들이 모여 나란 사람을 그려내는 선이 될 테니까.

Recent Posts
Recent Comments
Total
관리 메뉴

꿈꾸는리버리

struct와 class의 차이를 설명하시오. 본문

오뚝이 개발자/swift

struct와 class의 차이를 설명하시오.

rriver2 2022. 7. 6. 08:27
반응형

 1️⃣ Struct와 class의 공통점 

1) 값과 function을 저장할 properties와 methods 정의

: 변수 및 상수를 객체에서는 properties라고 하고, function을 methods라고 함

class Food {
    let name: String = "음식"
    func printName() {
        print("name : \(self.name)")
    }
}

 

2)  " . "을 사용하여 값에 대한 액세스할 수 있음

class Food {
    let name: String = "음식"
}

let food = Food.init()
food.name

 

3) 초기 상태를 설정하기 위한 initializer 정의

class Food {
    let name: String
    init(name: String){
        self.name = name
    }
}

 

4) 기본 구현 이상으로 기능을 확장을 위한 extention사용

class Food {
    let name: String
    init(name: String){
        self.name = name
    }
}

extension Food {
    var calorie: Int { return 100 }
}

 

5) 특정 종류의 standard function을 제공하는 protocols 준수

protocol FoodProtocol {
    func printName()
}

class Food: FoodProtocol{
    let name: String
    init(name: String){
        self.name = name
    }
    func printName() {
        print("name : \(self.name)")
    }
}

 

 2️⃣ Struct과 class의 차이점 

 - struct은 없지만 class만 가능한 것 

1) 한 클래스가 다른 클래스의 특성을 상속 가능

class Food { }
class Pizza: Food { }

 

2) Deinitializers를 사용하면 클래스의 인스턴스가 할당된 리소스를 해제 가능

class Food{
    let name: String
    init(name: String){
        self.name = name
    }
    deinit {
        print(name)
    }
}

var food: Food? = Food.init(name: "핏자")
food = nil
// "핏자"가 print 됨

 

3) 참조 카운팅은 클래스 인스턴스에 대한 둘 이상의 참조를 허용

: 기본적으로 참조 횟수는 2에서 증가하며, 참조 카운팅을 통해 class의 메모리가 할당된다.

 

4) Type casting 을 사용하면 런타임에 클래스 인스턴스의 유형을 확인하고 해석 가능

- is : Checking Type 

class Food { }
class Pizza: Food { }
class Yogurt: Food { }
 
let food: Food = .init()
food is Food      // true
food is Pizza      // false
food is Yogurt        // false

let pizza: Pizza = .init()
pizza is Food      // true
pizza is Pizza      // true
pizza is Yogurt        // false

- as : 업 캐스팅, 다운 캐스팅

class Food {
    let name: String = "음식"
}
class Pizza: Food {
    let kind: String = "치즈피자"
}
class Yogurt: Food {
    let kind: String = "플레인요거트"
}

// 1) 업캐스팅
    // 1. as를 사용한 업캐스팅
    let food1 = Pizza.init() as Food

    // 2. Type Annotation을 사용한 업캐스팅
    let food2: Food = Yogurt.init()

food1.name
food1.kind // 에러

// 2) 다운캐스팅
let food3: Pizza = food1 as! Pizza
food3.name
food3.kind

let food4: Pizza = food2 as! Pizza // 에러

 

 -  Class는 참조 타입, Struct는 값 타입 

class FoodClass{
    var name: String
    var calorie: Int
    init(name: String, calorie: Int){
        self.name = name
        self.calorie = calorie
    }
}

struct FoodStruct{
    var name: String
    var calorie: Int
    init(name: String, calorie: Int){
        self.name = name
        self.calorie = calorie
    }
}

let food1 = FoodClass(name: "핏자", calorie: 100)
let food2 = FoodStruct(name: "핏자", calorie: 100)

 

아래의 그림은 위의 코드를 메모리 측면에서 표현해 본 거다.
큰 검정 테두리 사각형은 ios 메모리를 나타낸 것이고 왼쪽은 class, 오른쪽은 struct 일때이다.

그림 해석)

class는 참조 타입이다!

지역 상수인 food1은 stack에 할당되고, 실제 Food 인스턴스는 heap에 할당이 된다.

따라서 스택에 있는 food1 안에는 힙에 할당된 인스턴스의 주소값이 들어있어 인스턴스를 참조하고 있다.

struct는 값 타입이다!

지역 상수인 food1은 stack에 할당될 뿐만 아니라, struct의 프로퍼티들도 stack에 할당된다.

 

💡 stack은 각각 thread가 독립적으로 할당이 되는 반면에 heap은 쓰레드가 공유하는 메모리 공간이다. 따라서 class의 경우 여러 thread가 Food 인스턴스가 있는 heap으로 접근이 가능하다. 따라서 "struct가 class에 보다 멀티 스레드 환경에서 Side-Effect가 없는 프로그램을 작성하는데 용이하다"

 

 

여기까지는 okay,,

 

1) 인스턴스를 복사해서 넣으면 ?!

// class
var food1 = FoodClass(name: "핏자", calorie: 100)
var food_1 = food1
food1.name = "핏자유~"
food1.name  //"핏자유~"
food_1.name //"핏자유~"

// struct
var food2 = FoodStruct(name: "핏자", calorie: 100)
var food_2 = food2
food2.name = "핏자유~"
food2.name  //"핏자유~"
food_2.name //"핏자"

 

이렇게 된다!

 

class의 경우에는 해당 인스턴스를 복사했을 때 주솟값을 복사하기 때문에, food1.name을 바꿨을 때 food1과 food_1 모두 변경된 값이 적용되는 반면에, 

stack의 경우에는 인스턴스의 프로퍼티들의 값을 복사하기 때문에, food2.name을 바꾸더라도 food_2에 변경된 값이 반영되지 않는다.

 

1 - 2 ) Identity Operators

===, !== 연산자는 해당 인스턴스의 내용 뿐만이 아니라, 주솟값의 일치 유무도 확인할 수 있다.

// class
var food1 = FoodClass(name: "핏자", calorie: 100)
var food_1 = food1
var food__1 = FoodClass(name: "핏자2", calorie: 100)
food1 === food_1  // true
food1 === food__1 // false

 

2) 인스턴스를 let으로 선언한 경우 내부 프로퍼티의 변경 유무

// class 
let food1 = FoodClass(name: "핏자", calorie: 100)
food1.name = "핏자유"
food1.calorie = 50

// struct 
let food2 = FoodStruct(name: "핏자", calorie: 100)
food2.name = "핏자유" // error
food2.calorie = 50  // error

class는 food1 인스턴스의 내부 프로퍼티인 name과 calorie을 변경할 수 있는 반면에,

struct의 경우에는 name과 calorie가 변경할 수 없다.

 

 

나는,., 이 둘 다 이상했다.

일단, class와 struct의 내부 프로퍼티들이 상수가 아닌 변수로 선언되었음을 확인하기는 했지만,,,

class의 인스턴스가 let인데  변경 가능한거지?

근데 또 왜 struct는 안되는 거지..? ㅋㅅㅋ....

 

그 이유는 class는 참조 타입이고 struct는 값타입이기 때문이다.

 

인스턴스를 let으로 선언했을 때,

class는 Food 인스턴스의 주솟값을 변경 못하게 되고 주솟값이 가리키고 있는 힙의 내부 프로퍼티들은 변경할 수 있다.

반면, struct는 stack에 struct의 프로퍼티들도 할당되어 있기 때문에 내부 프로퍼티도 변경이 되지 못한다.

이러한 이유 때문에 class는 let으로 선언되어도 변경 가능성이 잔존하게 된다. 이 때문에

💡 사람들이 "Immutable해야할 경우 struct을 쓰는 게 좋아요 ~"라고 하는 것이다...

 

+ ) 당연하게 아래와 같이 인스턴스를 상수가 아닌 변수로 선언을 하면 struct도 변경이 가능하게 된다.

// class 
var food1 = FoodClass(name: "핏자", calorie: 100)
food1.name = "핏자유"
food1.calorie = 50

// struct 
var food2 = FoodStruct(name: "핏자", calorie: 100)
food2.name = "핏자유"
food2.calorie = 50

 

 - class에서는 init 필수 

// class ) init 필수

// 에러 발생
//class FoodClass{
//    var name: String
//    var calorie: Int
//} 

class FoodClass{
    var name: String
    var calorie: Int
    init(name: String, calorie: Int){
        self.name = name
        self.calorie = calorie
    }
}

// struct ) init 없어도 됨
struct FoodStruct{
    var name: String
    var calorie: Int
}

 

💡 스위 프트의 다른 기본 타입(Bool, Int, Array, Dictionary, Set 등등)도 String 타입과 마찬 가지로 모두 구조체로 구현되어 있다. 따라서 기본 데이터 타입은 모두 값 타입이다. 전달인자를 통해 데이터를 전달하면 모두 값이 복사되어 전달될 뿐, 함수 내부에서 아무리 전달된 값을 변경해도 기존의 변수나 상수에는 전혀 영향을 미치지 못한다는 의미이기도 하다. 이런 점을 더욱 확실히 하기 위해 스위프트의 전달인자는 모두 상수로 취급되어 전달되는 것 같기도 하다.

 

 

 정리 

struct와 class의 공통점

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

 

struct와 class의 차이점

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

 

애플은 가이드라인에서 다음 조건 중 하나 이상에 해당된다면 구조체를 사용하기를 권한다.

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

+) 언제 struct를 쓰는게 좋을까요 ?

1. Immutable해야할 경우

2. 공유될 필요가 없는 경우

3. 대입보다는 생성이 많이 되는 타입의 경우

 

느낀점

우선, 다른 언어와 달리 struct에도 method가 들어갈 수 있어서, struct와 class의 경계선이 좀 더 가까워진 느낌이었다. 그래서 이런 질문을,, 하는 거겠지? 

struct와 class의 가장 다른 점 2가지는 주소 참조인가, 값 참조인가와 class의 상속 가능이다. 이로 인해 파생되는 특징들을 알아보면서 많은 것을 배울 수 있는 시간이었던 것 같다 ! 오늘도... 화이팅 !

 

출처 : 

ios 면접 질문

 

GitHub - JeaSungLEE/iOSInterviewquestions: 👨🏻‍💻👩🏻‍💻iOS 면접에 나올 질문들 총 정리

👨🏻‍💻👩🏻‍💻iOS 면접에 나올 질문들 총 정리 . Contribute to JeaSungLEE/iOSInterviewquestions development by creating an account on GitHub.

github.com

https://blog.yagom.net/530/

 

Swift - 구조체 클래스 - yagom's blog

yagom's blog Swift - 구조체 클래스

blog.yagom.net

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

 

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

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

https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes

 

Apple Developer Documentation

 

developer.apple.com

 

반응형

'오뚝이 개발자 > swift' 카테고리의 다른 글

Xcode SwiftLint 적용하기  (0) 2022.07.11
assert(_:_:file:line:)  (0) 2022.07.10
removeLast() vs popLast()  (0) 2022.05.24
[2/2] optional chaining  (6) 2022.05.14
[1/2] Optional unwrapping  (0) 2022.05.14
Comments