본문 바로가기
Swift

MetaType_iOS

by SeoB-P 2022. 5. 5.

메타타입이란?

Type의 Type임. - 애플 . 

???

1. Type 그 자체

2. Type을 다시 추상화?

3. 언제씀? Type 그 자체를 사용하고 싶을 때.

라고.. 하는데 잘 모르겠어서 하나하나 단어를 분해해보았다.

 

Meta가 뭘까?

메타(meta)란...

메타는 위키백과에서 많은 의미를 가지는데 이중에서 눈여겨 봐야할 것은 스스로 인 것 같다.

Type 그 자체(스스로) 뭔가 값을 가지고 있는 Type이라고 이해를 일단 해보았다.

위키 - 메타

 

Type이 뭘까?

Type is like MBTI - 포프리

출처 - http://news.bizwatch.co.kr/article/consumer/2020/05/08/0003

Type이란 어떠한 속성들을 모아서 분류하여 설명하는 것이라고 할 수 있다.

예를들어 그림의 MBTI를 보면, ISTJ라는 타입은 세상의 소금형, 한번 시작한 일은 끝까지 해내는 사람

이라는 속성을 가지고 있고, 이러한 속성들을 하나의 분류로 만들어 ISTJ로 설명하는 것이다.

따라서,,각각의 MBTI들이 하나의 타입이다!

그리고 이러한 타입을 가지고 있는 사람들이 있다.

프로그래밍적으로 보자면 각각의 MBTI가 하나의 Type이 되고

Type을 따르는 사람들은 인스턴스가 될 것 이다.

Type의 Type

Type의 Type은 뭐란 말인가???

ESFP의 ESFP??

 

 

우리는 보통 Type(Class, Struct..)을 이용해서 인스턴스를 만든다.

 

let a = A()   //A클래스의 인스턴스 a

 

그리고 인스턴스를 이용해서 어떠한 로직을 처리한다.

func printSomeThing(a: A) {
	 print(a)
}

 

하지만, 예를 들어서 어떠한 행사가 있는데 거기에는 MBTI중에서 E로시작하는 사람들만 들어갈 수 있다고 해보자.

이때, 행사를 참여 할 수 있는 사람들을 어떻게 판단할까?

만약 나라면, 사람들의 MBTI Type을 비교해서 E로 시작하는 지 아닌지를 판단을 할 것이다.

이때, 메타타입이 사용이 되는데 MBTI를 가지고 있는 사람(인스턴스)를 비교하는 것이 아니라

타입의 타입을 비교를 해야하는 것이다 !

물론 현실에서는 타입만 확인 하면 가능하겠지만, 우리 컴퓨터는 항상 멍청하다. 명확하게 말해주어야 한다.

변수에 타입의 타입 즉 타입 자체 = MetaType를 넣어주어야 할 때가 있다.

이를 pseudocode 코드로 짜보면..

func canJoin(type:MBTI.Type) -> Bool {
	if type.start == (E) {
		return true
	} else { 
		return false
	}
}

솔직이 위 설명은 억지로 끼워맞춘 느낌이 있다..

하지만 비슷한 느낌을 받았다면 오케입니다~

 

ObjectIdentifier.

타입을 따르는 인스턴스는 얼마든지 추가로 생성이 될 수 있지만, 타입은 한번 추가가 되면 더 이상 생성되지 않고 고유해진다.

따라서 Swift에서는 고유한 값인 ObjectIdentifier를 가질 수 있다고 한다!

이를 이용하면 Type들끼리 비교가 용이해진다.

코드로 Type과 인스턴스에 관련된 메서드를 확인해보자.

import UIKit


//MetaType?
class A {  }

let a = A()
let b = A()

//Object(객체,Type) 자체의 고유한 값(identifier)
print(ObjectIdentifier(a))   //ObjectIdentifier(0x0000600002884140)
print(ObjectIdentifier(b))   //ObjectIdentifier(0x000060000289c0c0)

//Type Property & instance Method
class Gucci {
    static let price = "expensive"
    
    func howMuch(bagName:String,pirce:Int) {
        print("\\(bagName)")
    }
}

//Type property
let price = Gucci.price

//instance Method
let gucciStore = Gucci()
let buyBag = gucciStore.howMuch(bagName: "Somthing", pirce: 200000000)

//type of
let price0 = type(of: gucciStore)  // Gucci.Type <= 메타타입!!
		price0.price                   //타입 그자체이기 때문에 Type Property 사용가능

 

그렇다면 메타타입은 언제 사용하나?

밑과 같은 코드가 있다고 해보자.

//MARK: -- Using Enum
//MARK: -- 뭔가를 print하는 프로토콜.
protocol SomeProtocol {
    func printSomething()
}

//MARK: -- 프로토콜을 따르는 구체 타입
class SomeClass:SomeProtocol {
    func printSomething() {
        print("Some!!")
    }
}

class AnotherClass:SomeProtocol {
    func printSomething() {
        print("Another!!")
    }
}

enum ClassName:String {
    case some = "SomeClass"
    case another = "AnotherClass"
}

이때 프로토콜을 준수하는 Class를 생성하는 Factory를 만들 때

우리는 주로 하드코딩을 피하기 위해서 ClassName이라는 Enum을 또 선언해서 .some과 같은 방식으로

활용을 한다.

//MARK: -- 매개 변수에 따라서 필요한 클래스를 만듬.
class Factory {
    func makeClass(className:ClassName) ->SomeProtocol? {
        switch className {
        case .some:
            return SomeClass()
        case .another:
            return AnotherClass()
        }
    }
}

//MARK: -- 실행부
let factory = Factory()

let someClass = factory.makeClass(className: .some)
let anotherClass = factory.makeClass(className: .another)

someClass?.printSomething()
anotherClass?.printSomething()

하지만, 우리는 MetaType을 활용하면 그렇지 않아도된다!

Type은 그 자체로서 고유한 값을 가질 수 있기 때문이다.

//MARK: -- Using MetaType

//MARK: -- Protocol
protocol SomeProtocol{
    func printSomething()
}

//MARK: -- Concrete Type
class SomeClass:SomeProtocol {
    func printSomething() {
        print("Some!!!")
    }
}

class AnotherClass:SomeProtocol {
    func printSomething() {
        print("Another!!!")
    }
}

ClassName이라는 Enum값은 사라지고 대신 인자값으로 .Type 즉, 타입 그 자체를 받음을 알 수있다.

위 코드와 정확히 같은 동작을 하지만 Enum이라는 또 다른 Type을 만들어 주지 않아도 되고

이렇게 MetaType을 적절히 활용을 하면

Enum의 Case가 늘어났을때를 대비해서도 더 유지보수가 쉬워질 수 있다는 장점이 있다.

//MARK: -- Factory
class Factory {
    
    func makeFactory(type:AnyObject.Type) -> AnyObject? {
        switch type {
        case is SomeClass.Type:
            return SomeClass()
        case is AnotherClass.Type:
            return AnotherClass()
        default:
            return nil
        }
    }
}

let factory = Factory()

let someClass = factory.makeFactory(type: SomeClass.self) as? SomeClass
let anotherClass = factory.makeFactory(type: AnotherClass.self) as? AnotherClass

someClass?.printSomething()
anotherClass?.printSomething()

 

 

참고: https://babbab2.tistory.com/151

https://babbab2.tistory.com/152?category=828998

https://onelife2live.tistory.com/49

댓글