티스토리 뷰

iOS

[Swift] Struct와 Class 비교하기 (2)

Sueaty 2021. 12. 8. 20:31

의도치 않게 (2)가 되어버린 포스팅입니다. 예전에 struct와 class의 비교를 표로 정리해서 올린 글이 있습니다. 이래저래 정리를 하고 나름대로 공부도 하며 정리를 했는데 예제도 없이 정리를 해서 머리에 잘 들어오지 않았던 탓에 조금 기억에 더 오래 남을 수 있도록 직접 코드를 쓰며 비교를 해보고자 합니다. 그래서 이번 포스팅에서는 class에서 할 수 있는 것들을 정리한 후에 같은 것을 struct로 바꾸면 어떤 차이가 있는지 비교해보겠습니다. 

 

요즘 크리에이터라는 직업이 핫하니 Creator class를 생성해보았습니다.

우선 크리에이터 이름이 필요할테니, name 이라는 instance property가 필요할 것 같아요.

Creator 객체를 새로 생성할 땐 항상 이름을 지정하도록 initializer를 만들었습니다. class 내부의 생성자나 메소드에서는 현재의 객체에 직접 접근이 가능합니다. self 라는 키워드를 통해 접근을 할 수 있는데, 아래 코드의 생성자 내부에 쓰인 self.name = name은 "내가 만든 이 객체의 name property에 parameter name값을 저장해줘!"라는 의미입니다.

class Creator {

   let name: String
   var platform: String
   
   init(name: String, platform: String) {
      self.name = name
      self.platform = platform
   }
   
   func information() {
      if platform == "Twitch" {
         print("\(name) is a creator in Twitch💜")
      } else if platform == "YouTube" {
         print("\(name) is a creator in YouTube♥️")
      }
   }
}


let oking = Creator(name: "Oking", platform: "Twitch")
oking.information()

let emma = Creator(name: "Emma Chamberlain", platform: "YouTube")
emma.information()

// 출력
// Oking is a creator in Twitch💜
// Emma Chamberlain is a creator in YouTube♥️

국내에서 크리에이터들이 활동하는 플랫폼은 제한적이므로 범위를 제한해주면 좋을 것 같습니다. 그래서 enum을 통해 그 범위를 제한하고,  platform의 타입을 Platform으로 지정해두도록 하겠습니다. 그러면 이제 switch문으로 information 내부에 있는 코드를 수정할 수 있겠죠? 조금 더해서 computed property를 활용해서 하트를 붙여서 return 해주고 information 출력을 이어나가보겠습니다. computed property는 실질적으로 값을 저장하지 않기 때문에 외부 요인에 의해 영향을 받는 변수에 사용하면 됩니다. 그리고 당연히 값을 저장하지 않기 때문에 값을 set을 할 수는 없습니다.

enum Platform {
   case Twitch
   case YouTube
}

class Creator {

   let name: String
   var platform: Platform
   var platformString: String {
      switch platform {
      case .Twitch:
         return "Twitch💜"
      case .YouTube:
         return "YouTube♥️"
      }
   }
   
   init(name: String, platform: Platform = .Twitch) {
      self.name = name
      self.platform = platform
   }
   
   func information() {
      print("\(name) is a creator in \(platformString)")
   }
}


let oking = Creator(name: "Oking")
oking.information()

let emma = Creator(name: "Emma Chamberlain", platform: .YouTube)
emma.information()

// 출력
// Oking is a creator in Twitch💜
// Emma Chamberlain is a creator in YouTube♥️

사용자 지정에 따라 하트의 수를 좀 바꿔보면 플랫폼 애정도를 나타낼 수 있지 않을까요?ㅎㅎ 새로운 변수 numberOfHearts를 입력받고 String(repeating: count: )를 활용해서 하트 개수를 변경할 수 있습니다. 그러나 만약 사용자가 하트의 갯수를 100개로 지정하면 너무 많겠죠? 그렇기 때문에 어느정도 조정을 할 필요가 있으니 numberOfHearts 변수에 getter setter property를 추가해서 구현할 수 있습니다. 그러나 getter, setter를 둔다는 것은 변수를 computed property로 사용한다는 것이기 때문에 외부에서 사용자가 값을 지정을 못해요. 그래서 일반적으로 _ (underscore)를 사용해서 추가 변수를 더 두고 사용하게 됩니다. 그리고 _numberOfHeart는 외부에서 직접 접근을 막기 위해 접근 제어자 private을 두어 제한할 수 있죠.

enum Platform {
   case Twitch
   case YouTube
}

class Creator {
    
   let name: String
   var platform: Platform
   var platformString: String {
       switch platform {
       case .Twitch:
           let heart = String(repeating: "💜", count: _numberOfHeart)
           return "Twitch" + heart
       case .YouTube:
           let heart = String(repeating: "♥️", count: _numberOfHeart)
           return "YouTube" + heart
       }
   }
    
   private var _numberOfHeart: Int = 1
   var numberOfHeart: Int {
       get { return _numberOfHeart }
       set { _numberOfHeart = newValue > 5 ? 5 : newValue }
   }
     
   init(name: String, platform: Platform = .Twitch) {
       self.name = name
       self.platform = platform
   }
    
   func information() {
       print("\(name) is a creator in \(platformString)")
   }
}


let oking = Creator(name: "Oking")
oking.numberOfHeart = 10
oking.information()

let emma = Creator(name: "Emma Chamberlain", platform: .YouTube)
emma.numberOfHeart = 3
emma.information()

// 출력
// Oking is a creator in Twitch💜💜💜💜💜
// Emma Chamberlain is a creator in YouTube♥️♥️♥️

 하트 기준 개수를 5개로 뒀는데 raw value를 사용하는 곳이 많아지면 나중에 코드를 수정할 때 손봐야 할 곳이 많아지고, 헷갈릴 수도 있습니다. 그러니 저 5라는 값을 class 내부의 static 변수로 두고 활용을 하면 좋을 것 같으니

static let maxHearts = 5

을 추가해 주고 '5'가 적힌 부분들을 Self.maxHearts 로 수정하면 됩니다.

이전의 self 와 다르게 이번에는 Self 를 사용하게 되는데, self는 instance의 property에 접근하기 위해 사용되고 Self는 class 자체에 속하는 상/변수/메소드에 접근하기 위해 사용하기 떄문입니다. maxHearts는 static 상수로 class에 속하기 때문에 Self 키워드를 사용해야합니다.

 

이제 저희가 작성한 코드를 구조체로 바꿔보겠습니다. 어떤 것들을 바꿔야 하고 어떤 변경사항들이 생기는지 보도록 하죠.

 

우선 가장 먼저 시작할 부분은 class 키워드를 struct 키워드로 바꾸는 것입니다. 그 즉시 바로 아래와 같은 에러를 맞이합니다. class는 특이(?)하게도 let을 통해 instance를 생성하더라도 내부 property들을 수정할 수 있지만 struct는 그렇지 않습니다. 그렇기 때문에 property에 접근해서 수정하기 위해서는 var로 인스턴스를 생성해야합니다. (사실 상수로 만들었는데 수정이 가능한게 조금 이상하다고 느껴지기는 해요. 그렇지 않나요?)

class와 struct의 차이에 대해 논하자면 항상 1번으로 나오는 주제가 type인 것 같습니다. Reference type vs. Value type이죠. 대학교 1학년 때 배우는 C를 할 때부터 지겹도록 들어왔지만 멀고도 가까운 type에 대한 이야기... 발그림으로 간단히 설명해보자면 아래와 같습니다. (iPad를 들고나오지 않아 마우스로 그렸더니 정말 발그림이....ㅎㅎ) class는 reference type 즉, 메모리 공간에 있는 data에 참조를 하고 있는 형태입니다. 그래서 한 인스턴스가 수정을 하면 다른 인스턴스도 영향을 받습니다. 반면에 struct는 복사를 하게 되므로 별개의 객체라서 변경을 해도 영향을 받지 않습니다.

이 외에도 class는 subclassing이 가능하지만(상속이 가능하지만) struct는 그게 안된다는 점, 프로퍼티를 변경시키는 내부 메소드의 경우 struct는 mutating 키워드를 붙여야 하다는 점 등이 있죠! 생성자도 struct는 없어도 괜찮고 원한다면 init을 별도로 생성할 수도 있습니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함