2015년 1월 15일 목요일

[Swift] 좀 더 단순한 싱글턴 패턴(Singleton Pattern)

과거에 쓴 dispatch_once 및 static struct 를 사용한 싱글턴 패턴은 외워서 쓰기가 좀 귀찮다. 라인수가 조금은 기니깐. 그래서 찾아보니 의외로 단순하게 Swift의 클래스에 싱글턴 패턴을 적용 할 수 있는 방법을 찾았다.

매우 단순하게도, 고전적인 '전역변수를 이용한 방식'이다.
private let _someClassSharedInstance = SomeClass()

class SomeClass {
    class func sharedInstance() -> SomeClass {
        return _someClassSharedInstance
    }
}
그야말로 심플하다. :-)

이 방식이 위험해 보인다면 일단 Objective-C 시절 스레드 등의 병렬 프로그래밍에서 문제를 겪어봤거나 들어본 사람일 것이다.

하지만 걱정하지 말자. Swift에서 let으로 선언된 변수는 일단 thread-safe가 기본적으로 보장된다. 상수(Constants)이기 때문에 최초의 값 대입만 허용된다는 점을 생각해보자.

그리고 Swift의 전역변수는 lazy 하게 처리된다. 즉, 위 sharedInstance() 클래스 메소드가 한 번이라도 호출되지 않으면 인스턴스가 아예 생성되지도 않는다. 따라서 인스턴스 생성 시점은 첫 싱글턴 팩토리 메소드가 호출 될 때 이며, 사용하지 않으면 메모리 낭비도 없다는 의미이다.

결론적으로 기존 dispatch_once 방식과 비교해 코드는 간단해지고 가독성도 좋아지며 능력은 비슷한 코드가 되었다.

참고로 Swift 클래스의 경우 class var를 사용 할 수도 있기 때문에 아래와 같이 메소드가 아닌 프로퍼티 형태로 싱글턴 인스턴스를 넘겨주는 방식도 가능하다.
private let _someClassSharedInstance = SomeClass()

class SomeClass {
    class var sharedInstance: SomeClass {
        return _someClassSharedInstance
    }
}
둘 중 어느 방식이 좋냐하면 호불호가 갈리겠다.
  • 기존 파운데이션의 방식은 메소드 방식이라 익숙하다.
  • 하지만 프로퍼티 방식이 괄호를 덜 쳐도 되니 편하다.
뭐 좋은 대로 고르자.

업데이트 -_-

새로운 방법을 발견하였기에 업데이트 한다. class 내부에서 static 프로퍼티를 사용 할 수 있기 때문에 아래와 같은 식으로 싱글톤을 만들어 두는 방법이 있다.
class SomeClass {
    static let sharedInstance = SomeClass()
}
앞서 본 예와는 큰 차이는 없어보인다. 다만 글로벌 네임스코프를 벗어나는 좋은 방식(?)이기 때문에 이 마지막 방법이 가장 좋은 예 같다. 어차피 코드도 한 줄이라 제일 간단하기도 하고...
PS. 물론 알겠지만 struct는 싱글턴 패턴을 못 쓴다. 레퍼런스 복사가 불가능 하기 때문에...

[관련글] Swift - 싱글턴 패턴(Singleton Pattern) (dispatch_once 및 static struct 버전)
[관련글] Swift Memory Management #3 - 구조체(struct)와 클래스(class)
[관련글] 스위프트(Swift) 가이드

댓글 없음 :