주로 네트워크 관련 코드 또는 외부 라이브러리 코드를 보면 shared, standard, defaults로 프로퍼티 또는 메서드에 접근하고 있는 코드를 볼 수 있다. 이런 코드들은 싱글톤 패턴을 적용해 만들었기 때문인데, 싱글톤 패턴이 무엇인지 알아보자.
Singleton
싱글 .. 벙글 .. 이 아니고 싱글톤, 이름에서 알 수 있는 것처럼 어떤 것을 '하나'로 관리/유지하고 있다는 것을 알 수 있다.
싱글톤에 대한 정의를 찾아보면 아래와 같다.
The singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance.
즉, 한 개의 클래스로 만드는 객체는 단 하나여야 한다.는 규칙을 갖고 있는 디자인 패턴이다.
.. 무슨 말이냐면,
프로젝트(= 프로그램)에 단 하나의 전역 객체를 만들어 놓고 외부에서 이 하나의 객체에만 접근할 수 있도록 하는 것이다.
= 특정 용도로 객체를 하나만 생성해서 공용으로 사용하고 싶을 때 사용하는 디자인 유형
예시 코드를 통해서 알아보자면, 아래와 같은 코드를 본 적이 있을 것이다.
URLSession.shared
서버 통신을 할 때, iOS에서는 URLSession 객체를 사용하는데 이미 만들어져 있는 shared 객체에 접근을 해서 메서드를 수행할 수 있다.
UserDefaults.standard
UserDefaults에서 기본적으로 공유하고 있는 standard 저장소에 접근해서 읽고-쓸 수 있는 standard에 접근을 해서 메서드를 수행할 수 있다.
이런 코드들 모두 싱글턴 패턴을 기반으로 만들어졌다고 할 수 있다.
iOS에서는 언제 싱글톤 패턴을 사용할까?
let screen = UIScreen.main
let userDefault = UserDefaults.standard
let application = UIApplication.shared
let fileManager = FileManager.default
let notification = NotificationCenter.default
싱글톤 패턴을 구현하고 싶다면 아래와 같이 구현하면 된다.
class SingletonExample {
static let shared = SingletonExample()
private init { }
}
static 키워드를 사용해서 클래스 외부에서, 프로젝트에서 전역적으로 사용할 수 있도록 만들고
private init 을 통해서 다른 곳에서 마음대로 초기화 및 객체로 만들 수 없도록 설정한다.
이렇게 만들면 해당 프로젝트에서는 SingletonExample에 대한 객체는 shared 하나만 존재한다고 할 수 있다. (물론 몇가지 특수한 상황, 예를 들면 멀티 쓰레드,에서는 반드시 그렇다고 보장할 수 없다.)
싱글톤 패턴의 특징
장점
싱글톤 패턴을 사용하면 무엇이 좋을까?
- 메모리를 단 한번만 사용할 수 있다.
프로젝트에서 해당 객체는 하나만 존재한다. 즉, 한번만 만들어지기 때문에 메모리 관리 역시 보다 수월해진다. 한번 만들어둔 객체는 꾸준히 재사용할 수 있다. - 객체 접근 시간이 줄어든다.
다시 메모리에 할당하고, 초기화 하는 과정이 줄어들기 때문에 한번 만들어두면 다시 접근할 때 매번 객체를 만드는 것보다 시간이 짧다. - 전역 범위에서 상태, 데이터 전달이 쉬워진다.
어디서든 접근할 수 있기 때문에, 어떤 상태를 표시 및 데이터를 공유하기에 적합하다.
단점
그러나, 역시 단점도 존재한다.
- 테스트가 힘들다.
싱글톤 패턴은 주로 위에서처럼 init을 private으로 설정하여 유일성을 보장하는 경우가 많다. 그런 측면에서 보면 좋지만, 테스트용 mock 객체를 만들기 어렵다. - 의존성을 만들어낸다.
클래스 외부에서 어디든지, 쉽게 접근할 수 있기 때문에 프로젝트의 여러 곳에서 사용될 수 있다. 이렇게 남발하게 되면 나중에 문제가 생겼을 때 어디서 생긴 것인지 확인하기 어려워진다.
-> 싱글톤 인스턴스가 너무 많은 일을 하거나 많은 데이터를 공유할 경우 다른 클래스의 인스턴스 간에 결합도가 높아져서 "개방-퍠쇄 원칙"을 위배하게 된다. - 멀리 쓰레드 환경에서 위험할 수 있다.
앞서 말한 특수한 경우 중 하나로 멀티 쓰레드 환경에서 싱글톤 객체가 2개 .. 생기는 문제가 발생할 수 있다. 또한 싱글톤 객체 내부의 데이터를 동기적으로 처리하지 않으면 여러 객체가 동시에 사용하다가 꼬여버릴 수 있다.
그렇다면, 실제로 어떻게 사용하는 것이 좋을까?
예를 들어서 User 정보를 저장하는 클래스를 만들어보자.
class UserInfo {
var id: String?
var password: String?
var name: String?
}
그리고 이 유저 정보를 총 3개의 ViewController에서 접근하여 관리한다고 할 때,
// A ViewController
let userInfo = UserInfo()
userInfo.id = "So-Kyte"
// B ViewController
let userInfo = UserInfo()
userInfo.password = "123"
// C ViewController
let userInfo = UserInfo()
userInfo.name = "So-Kyte"
위와 같이 세 개의 ViewController에서 각각 UserInfo 객체를 만들어 저장을 하게 되면 각 인스턴스의 프로퍼티에만 저장이 될 것이다.
그러면 이는 1명의 유저를 관리하는 것이 아니라 3명의 유저를 관리하는 것처럼 동작될 것이다.
이럴 때, 이 클래스에 대한 인스턴스는 최초 생성될 때 한번만 생성해서 전역에 두고 이후로는 인스턴스에 접근하여 프로퍼티 또는 메서드를 사용하도록 하는 것이다. 이 방법이 바로 싱글톤 패턴이다.
(= 한 인스턴스를 어떤 클래스에서든 접근 가능하도록 만드는 것)
위의 UserInfo 클래스에 대해서 싱글톤 패턴을 적용하면 아래와 같다.
class UserInfo {
static let shared = UserInfo()
var id: String?
var password: String?
var name: String?
private init() { }
}
1. 먼저 전역으로 저장될 것이기 때문에 static을 사용해서 인스턴스를 저장할 프로퍼티를 하나 만든다.
2. 혹시라도 외부에서 init 함수를 호출해 인스턴스를 또 생성하는 것을 막기 위해 init() 함수 접근 제어자를 private으로 지정한다.
그리고 나서 각 ViewController에서 각각 따로 인스턴스를 만들었던 것을 아래와 같이, shared를 통해 하나의 인스턴스로 접근할 수 있도록 한다.
// A ViewController
let userInfo = UserInfo.shared
userInfo.id = "So-Kyte"
// B ViewController
let userInfo = UserInfo.shared
userInfo.password = "123"
// C ViewController
let userInfo = UserInfo.shared
userInfo.name = "So-Kyte"
'Swift' 카테고리의 다른 글
Closure (클로저) (0) | 2022.08.10 |
---|---|
First-Class Citizen(일급 객체) (0) | 2022.08.09 |
프로토콜 (0) | 2022.08.02 |
프로퍼티 - 저장 프로퍼티 (0) | 2022.07.27 |
some (0) | 2022.04.29 |