싱글턴 패턴
Last updated
Last updated
클래스 인스턴스를 하나만 만들고, 그 인스턴스로의 전역 접근을 제공한다.
싱글턴 패턴대신 그냥 전역변수를 사용하면 왜 안될까요 ? 전역변수에 객체를 대입하면 애플리케이션이 시작될 때 객체가 생성됩니다. 만약 그 객체가 자원을 많이 차지하고, 그 객체를 애플리케이션이 끝날 때까지 한번도 사용하지 않는다면? 자원낭비죠.
싱글턴 패턴을 사용하면 lazy하게(필요할 때만 생성되도록) 사용할 수 있습니다.
아래의 코드처럼 생성자를 private 으로 만들고, 타입 메소드를 통해 이미 1번 생성했다면 그대로 리턴하고 생성되지 않았다면 생성해서 리턴하는 것으로 유일한 객체를 사용할 수 있습니다.
하지만 아래의 코드는 문제가 있다.
If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property hasn’t yet been initialized, there’s no guarantee that the property will be initialized only once.
공식 문서에서도 lazy한 속성에 대해서는 한번만 초기화되어야 된다는 보장이 없다는 글이 있다.
즉, 아래의 그림과 같은 상태이다. 아직 객체가 초기화되기 전에, 여러 곳에서 접근해서 원래 의도와는 다르게 여러개의 객체가 생기는 경우이다.
Synchronization(동기화)
getInstance()를 동기화시켜버리는 것이다. 즉, 초기화 될 때까지 강제로 기다리게 해버려서 다음에 접근할 때는 안전하게 하는 방법이다. objc 같은 경우는 @synchronized 키워드가 있지만 swift에는 그런 키워드가 없다. 따라서 DispatchQueue.global().sync {} 같은 것을 사용해야 할 것이다.
다만, 메소드를 동기화하면 성능이 100배쯤 저하된다고 한다. 그건 어떻게 하지? 가장 간단한 해결책은 getInstance()의 속도가 그리 중요하지 않다면(큰 파일이 아니라면) 그냥 둔다.
lazy하게 사용하지 않고 그냥 처음부터 만들어버린다.
어찌보면 가장 간결한 해결책일 수 있다. 예전과는 다르게 하드웨어의 성능이 좋아져서 상대적으로 메모리가 넉넉하기 때문에, 아주 특정한 상황이 아니라면 괜찮을 것이다.
예시코드로 초콜릿을 만드는 초콜릿 보일러 코드가 있다.
초콜릿 보일러는 순서대로 채우고, 끓이고, 다음단계로 보낸다.
아래의 코드를 그대로 사용했더니, 아직 초콜릿이 끓고 있는 와중에 새로운 초콜릿이 들어가버리는 오류가 발생했습니다. 이 코드를 싱글턴 패턴으로 바꿔보자.
2번째 방법으로 문제를 해결했다.
그럼에도 불구하고 싱글턴 패턴은 느슨한 결합 원칙을 따르지는 않습니다. 싱글턴을 사용하다 수정하게되면 그에 따르는 클래스들도 고쳐야 하는 경우가 많기 때문이죠.
하나는 클래스는 하나의 책임을 따른다(SRP) 원칙 또한 따르지 않죠. 싱글턴은 자신의 인스턴스를 관리하는 일 외에도 원래 그 싱글턴을 사용하고자 하는 목적의 코드가 들어갈 거에요. 최소 2가지의 일을 하게 되는 것이죠. 그러나, 많은 개발자가 이미 싱글턴에 익숙하고 특정한 목적에 있어서 싱글턴을 사용하면 앱을 더욱 간단하게 만들 수 있기 때문에 사용합니다.
https://www.hanbit.co.kr/store/books/look.php?p_code=B6113501223