Search
Duplicate
🛠️

Swift의 타입과 타입의 타입

Created
2019/07/08
Tags
Programming
Swift
스위프트는 타입을 아주 중요하게 다루는 언어입니다. 어느 정도냐면.. 심지어 타입에도 타입이 있을 정도지요!! 그리고 이 타입의 타입을 메타타입이라고 부릅니다.
“타입의 타입”이 대체 뭔 소린지, 어디에 쓰일 수 있는지 바로 감이 오지 않을 수 있습니다. 타입만 해도 충분히 추상적인 개념인데, 그걸 또 한 번 더 추상화 시킨다니…뜬 구름 잡는 소리 처럼 느껴지는 것도 무리가 아닙니다.
하지만 차근차근 개념을 쫓아가다보면, 사실 별 것 아니란 걸 알게 되실 겁니다. 이번 포스트에서는 이 메타타입이 무엇이고 어떤 문제를 해결하는지 다뤄보도록 하겠습니다.

해결하고 싶은 문제

혹시 이런 상황에 부딪혀 보신 적 있으신가요?
class MyCell: UITableViewCell { ... } // MyCell이란 이름의 xib파일도 있음 let myCellNib = UINib(nibName:"MyCell", bundle: nil) tableView.register(myCellNib, forCellReuseIdentifier: "MyCell") --- class YourCell: UITableViewCell { ... } // YourCell이란 이름의 xib파일도 있음 let yourCellNib = UINib(nibName:"YourCell", bundle: nil) tableView.register(yourCellNib, forCellReuseIdentifier: "YourCell") // 기타 등등....
Swift
복사
딱 봐도 많은 코드가 중복되고 있습니다. 이 때 이런 생각이 들지 않나요?
‘아… MyCell이라는 타입 자체를 변수로 쓸 수 있으면 좋겠다…’
좋은 소식입니다! Swift에서는 바로 그걸 할 수 있습니다!

MyCell.self

우리는 종종 MyClass.self 같은 표기를 만날 때가 있습니다. 대표적으로 Codable들을 Encoding하거나 Decoding할 때 만나게 되죠.
let myInfo = try? JSONDecoder().decode(MyClass.self, from: data)
Swift
복사
이 때 MyClass.self에 해당되는 부분이, 바로 “MyClass”의 타입 자체의 변수 형태입니다.
“타입 자체의 변수 형태” 라고 하니까 감이 좀 잘 안 오는 면이 있습니다. 저는 아래 코드를 보니까 이해가 확 되더라고요.
class MyViewController: UIViewController { class var storyboardInstance: UIViewController { // eg. storyboardID 기반으로 ViewController생성 } var someProperty: Int = 0 } let myViewController = MyViewController() print(myViewController.someProperty) // Print 0 print(myViewController.storyboardInstance) // Can't Access!! print(MyViewController.someProperty) // Can't Access!! print(MyViewController.storyboardInstance) // 평소 쓰던 class 변수 접근법 print(MyViewController.self.storyboardInstance) // **위의 거랑 같은 거!!**
Swift
복사
그렇습니다. SomeClass.classVariable 은 SomeClass.self.classVariable의 줄임말 이었습니다. 우리는 SomeClass.self라는 표기법으로 Class변수(static 변수)에 접근 할 수도 있고, class메소드(static메소드)에도 접근 할 수 있습니다.
즉,  SomeClass.self의 표기법을 통해서, 우리는 “class변수와 class메소드를 가지는 어떤 객체”에 접근 할 수 있게 되는 것입니다.

type(of:type(of:)

한 걸음만 더 나아가 봅시다. 위의 코드에서 myViewController의 타입은 무엇일까요?
let myViewController = MyViewController() print(type(of:myViewController)) // print "MyViewController"
Swift
복사
그렇습니다. myViewController의 타입은 MyViewController 입니다. 그러면 MyViewController.self의 타입은 무엇일까요? MyViewController.self도 자신의 속성과 메소드를 지니는 객체인 이상, “이 객체의 타입은 무엇이지?”라는 질문이 자연스러워집니다. 바로 확인해 봅시다.
print(type(of:MyViewController.self)) // Print "MyViewController.Type"
Swift
복사
그렇습니다. MyViewController.self 의 타입은 MyViewController.Type 인 것이지요.
따라서 이렇게 쓸 수 있습니다.
let aType: MyViewController.Type = MyViewController.self
Swift
복사
여기서 MyViewController.Type을 타입의 타입, 즉 메타타입이라고 합니다.
let myVC = MyViewController() print(type(of:MyViewController.self)) // MyViewController.Type print(type(of:type(of:myVC)) // 위랑 같은 의미!!
Swift
복사
이런 메타타입에도 상속의 개념이 그대로 적용됩니다. 따라서 아래와 같이 쓸 수도 있습니다.
var aVCType: UIViewController.Type = MyViewController.self if aVCType == LoginViewController.self { // Do Login Process } else if aVCType == MyViewController.self { // Do MyVC Stuff... }
Swift
복사

결론

이상의 내용을 바탕으로, 맨 처음 우리가 당면했던 문제를 해결 해 볼까요?
extension UITableView { /// 전제조건 : cellType의 이름과 같은 xib파일이 있어야 함 public func register(_ cellType: UITableViewCell.Type) { let cellClassName = String(describing:cellType) let nib = UINib(nibName: cellClassName, bundle: Bundle(for: cellType)) register(nib, forCellReuseIdentifier: cellClassName) } public func register(cellTypes: [UITableViewCell.Type]) { cellTypes.forEach { register(cell: $0) } } } // 용례 tableView.register(MyCell.self) tableView.register([MyCell.self, YourCell.self])
Swift
복사
훨씬 깔끔하게 tableViewCell을 등록 할 수 있게 되었네요.
타입과 메타타입의 관계는 일단 이해하고 나면 별 것 아니지만, 굉장히 추상적인 개념이기 때문에, 이 글을 읽는 것 만으로는 헷갈림이 쉽게 가시지 않을 수 있습니다. 모쪼록 위의 코드 예제들을 조금씩 변형해서 Xcode Playground에서 다양한 형태로 만져보시기 바랍니다.

참고문헌

1.
Swift Meta-Type : 스위프트 공식 문서
2.
type(of:T) : type(of)함수의 애플 공식문서