Swift의 namespace

객체지향 생활체조에서 원시값을 모두 감싸라는 규칙에 관해 어떤 식으로 구현해야 할지 고민하고 있을 때 nameSpace에 관해 알아보라는 조언을 받았다. 

namespace란?

namespace를 구글링해보면 C++의 NameSpace가 가장 많이 나온다. C++의 namespace의 개념을 간단하게 말하면 같은 것 끼리 모은 공간을 만들고, 모은 공간이 다르면 다른 것으로 치는 것이라고 생각하면 될 듯하다. 네트워크의 NAT의 개념과 비슷하게 생각하면 될 듯? swift에서는 상수를 감싸서 상수 대신 감싼 변수명을 입력하는 식으로 사용한다.

왜 사용하는가?

iOS로 프로젝트를 진행하면 무수히 많은 상수를 사용합니다. 오토레이아웃을 잡을때, 애니메이션을 사용할 때, UserDefaults를 사용할 때 등등 상수를 사용할 때가 많습니다. 하지만 시간이 지나 다시 코드를 리팩토링 해야 할 때, 협업할 때 다른 사람이 내 코드를 볼 때 어떤 상수인지 보기 어렵거나 리팩토링 하기 어려움이 있을 수 있습니다. 이 문제를 해결하기 위해 알아보기 쉽게 상수를 감싸서 값을 직접 넣는 대신 감싼 이름을 넣어 줍니다.

구현 방법

struct, enum, class등 원하는 대로 구현해도 되지만 보통은 enum으로 많이 구현한다고 한다. nameSpace는 굳이 상수값을 제공하는 객체를 굳이 생성할 필요가 없다. 그래서 struct는 private init()을 사용하여 생성자를 숨겨야 하지만, enum은 case를 만들지 않으면 init을 사용할 수 없다. 또한 enum의 case는 원시값을 추가할 때 중복된 값을 추가 할 수 없으므로 보통 enum에 타입 프로퍼티로 선언해서 사용한다고 한다.

enum Metric {
    static let labelTraintConstraint: CGFloat = 10
    static let labelTopConstraint: CGFloat = 10
}

요런 식으로 타입 프로퍼티로 만들어서 사용한다.
enum은 rawValue (원시값)을 통해 namspace를 만들 수 있지만, case를 이용해야 하고, 채택한 타입만 rawValue로만 줄 수 있고, 무엇보다 코드 뒤쪽에. rawValue를 붙여야 해서 많이 선호하는 방식은 아닌 듯하다.

만약 타입 프로퍼티가 무수히 많아진다면

static은 프로그램이 시작하면 그때 초기화되고, 프로그램이 종료되어야지만 해제된다. 그렇다면 namespace가 무수히 많아진다면 그래도 정상적으로 프로그램이 실향될까 궁금해졌다.
만약 Metric에 타입 프로퍼티가 백만 개쯤 들었다고 가정해 보자.

enum Metric {
    static let labelTraintConstraint: CGFloat = 10
    static let labelTopConstraint: CGFloat = 10
.
.
.
.
}

이 상황에서 Metric을 호출하게 되면 바로 앱이 꺼지는 게 아닐까?라고 생각 할 수도 있지만 실제로는 그렇지 않다.
 

lazy 한 타입 프로퍼티

타입 프로퍼티는 인스턴스 프로퍼티와 다르게 자신이 속해있는 class나 struct, enum이 생성될 때 같이 초기화되는 것이 아닌, 처음 불렸을 때 초기화가 된다. 즉 lazy 하게 작동한다.

struct ABC {
    
    init() {
        print("ABC")
    }
}

struct BCD {
    init() {
        print("BCD")
    }
}

struct A {
    
    static let abc: ABC = ABC()
    static let bcd: BCD = BCD()
    
    init() {
        print("A")
    }
}

struct A가 초기화될 때, 타입 프로퍼티인 abc와 bcd는 초기화되지 않는다. 각 타입 프로퍼티가 호출될 때 그때 초기화 된다.
따라서 하나의 enum에 타입 프로퍼티가 수백만 개 들었다 해도 사용할 때 초기화 되니 메모리 걱정을 할 필요는 없을 것 같다.
 

타입 프로퍼티는 왜 let으로 선언할 수 있을까?

여기서 또 하나 의문이 생긴다. lazy 프로퍼티는 let으로 선언할 수 없다. 하지만 lazy하게 작동하는 static은 let으로 선언 할 수 있다. 왜 이런 차이가 발생할까?
swift 문서의 lazy stored properties를 보면 lazy 프로퍼티를 let으로만 선언 할 수 있는 이유를 다음과 같이 설명한다.

해석하자면 상수 속성은 객체가 초기화되기 전에 항상 값을 가지고 있어야 하는데, lazy는 객체가 초기화가 되고 난 후 초기화가 되는 것이 아닌 처음 사용될 때 초기화가 되는 것이기 때문에 let으로 선언할 시 값을 변경할 수 없으므로 var로 선언해야 된다고 한다.
즉 lazy를 사용한 인스턴스 프로퍼티는 객체를 생성한 후 사용하기에 var로만 사용할 수 있지만, 타입 프로퍼티는 객체의 생성 유무와 상관 업기에 static을 쓸 수 있는 것이다.