Focus On Develop 🀟🀟

[Swift] Singleton Pattern (싱글톀 νŒ¨ν„΄) λ³Έλ¬Έ

iOS [Swift]/기초λ₯Ό νƒ„νƒ„νžˆ!

[Swift] Singleton Pattern (싱글톀 νŒ¨ν„΄)

λˆ„λ¦¬λ‹¬μ΄ν‹€ 2021. 3. 16. 15:22

μ˜€λžœλ§Œμ— ν¬μŠ€νŒ…μ„ ν•˜λŠ” 것 κ°™λ„€μš”, 이런 μ €λŸ° μ‹ κ²½μ“Έ 일듀이 λ§Žμ•˜λ˜ 것 κ°™μ•„μš” πŸ˜‚

바빠도 ν•˜λ£¨μ— κ°„λ‹¨ν•œ ν¬μŠ€νŒ…μ΄λΌλ„ ν•˜λ‚˜λŠ” ν•˜μžκ³  λͺ©ν‘œλ₯Ό μ„Έμ› λŠ”λ°, 쉽지 μ•Šλ”κ΅°μš” 😭

κ±°λ‘μ ˆλ―Έν•˜κ³  μ˜€λŠ˜μ€ 싱글톀 νŒ¨ν„΄μ— λŒ€ν•΄μ„œ μ•Œμ•„λ³Όκ²Œμš”!!


Design Pattern

싱글톀 νŒ¨ν„΄μ€ Design Pattern 쀑 생성과 κ΄€λ ¨λœ λ””μžμΈ νŒ¨ν„΄μ΄μ—μš”. 그럼.. Design Pattern은 λ­˜κΉŒμš”?

κ°œλ°œμ—μ„œ λ””μžμΈνŒ¨ν„΄μ΄λž€, λ‹€μ–‘ν•œ κ°œλ°œν™˜κ²½μ—μ„œλ„ λΉ„μŠ·ν•œ λ¬Έμ œλ“€μ΄ λ°œμƒν•  수 μžˆλŠ”λ°, 이 λΉ„μŠ·ν•œ λ¬Έμ œλ“€μ„ ν•΄κ²°ν•˜λŠ” μ •ν˜•ν™”(?)된 ν…œν”Œλ¦Ώμ΄λ‚˜ κ°œλ°œνŒ¨ν„΄ 같은, 마치 "ν•΄κ²°μ±…" κ°™μ€κ±°μ—μš”. 이런 μ–΄λ–€ νŒ¨ν„΄μ„ μ μš©ν•˜λ©΄, 그만큼 "μ •ν˜•ν™”"된 ν•΄κ²°μ±… 같은 λ§₯λ½μ΄λ‹ˆ μ½”λ“œμ˜ 가독성, νš¨μœ¨μ„±, 디버깅, ν˜‘μ—… 등이 μ‰¬μ›Œμ§€κ² μ£ ?

 

Swiftμ—μ„œ κ°€μž₯ 보편적으둜 μ ‘ν•  수 μžˆλŠ” λ””μžμΈ νŒ¨ν„΄μ€

  • 생성 νŒ¨ν„΄ : Singleton
  • ꡬ쑰 νŒ¨ν„΄ : MVC, MVVM, Facade
  • 행동 νŒ¨ν„΄ : Delegate, Observer 

등등이 μžˆμ–΄μš”. 그쀑에 이번 ν¬μŠ€νŒ…μ—μ„œλŠ” Singleton Pattern에 λŒ€ν•΄ μ‚΄νŽ΄λ³΄λ €κ³  ν•΄μš”.

 

 

Singleton Pattern (싱글톀 νŒ¨ν„΄)

싱글톀 νŒ¨ν„΄μ΄λž€, νŠΉμ • μš©λ„λ‘œ 객체λ₯Ό ν•˜λ‚˜λ§Œ μƒμ„±ν•΄μ„œ 곡용으둜 μ‚¬μš©ν•˜κ³  μ‹Άμ„λ•Œ μ‚¬μš©ν•˜λŠ” 생성 λ””μžμΈ νŒ¨ν„΄μ΄μ—μš”.

주둜 ν™˜κ²½μ„€μ •, λ‘œκ·ΈμΈμ •λ³΄ λ“±μ˜ νŠΉμ • μš©λ„λ‘œ 생성해두고, μ•±μ˜ μ—¬λŸ¬ κ³³μ—μ„œ μ ‘κ·Ό κ°€λŠ₯ν•˜λ„λ‘ λ§Œλ“€μ–΄μ„œ 데이터λ₯Ό μ‚¬μš©ν•˜κ²Œλ” λ§Œλ“€ λ•Œ μ‚¬μš©λ˜μš”. 무엇보닀 μ˜ˆμ‹œλ₯Ό λ³΄λŠ”κ²Œ 제일 μ’‹κ² μ£ ~?

class Person {
    static let shared = Person()
    private var name: String = "KT"
    private var nickName: String = "Nury"
    private var age: Int = 50
}

μ΄λ ‡κ²Œ static property둜 sharedλΌλŠ” 객체λ₯Ό 클래슀 λ‚΄μ—μ„œ λ§Œλ“€μ–΄ μ£ΌλŠ” 생성 νŒ¨ν„΄μ΄μ—μš”.

μ΄λ ‡κ²Œ 해두면 μ•±μ˜ μ–΄λ””μ„œλ“  Person.shared 객체둜 클래슀의 데이터에 μ ‘κ·Ό ν•  수 있겠죠?

 

음.. μ’‹μ•„!

근데 νŠΉμ • μš©λ„λ‘œ μ‚¬μš©ν•  객체λ₯Ό "ν•˜λ‚˜"만 μƒμ„±ν•΄μ„œ μ“°λŠ”κ±°λΌλ©°? λ‹€λ₯Έλ°μ„œ let person = Person() ν•˜λ©΄ λ˜λŠ”κ±° μ•„λ‹ˆμ•Ό?

λ„€, λ§žμŠ΅λ‹ˆλ‹€! κ·Έλž˜μ„œ 클래슀 μ™ΈλΆ€μ—μ„œ μƒˆλ‘œμš΄ 객체λ₯Ό 생성할 수 없도둝, μƒμ„±μž(init)을 private둜 λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•΄μ€λ‹ˆλ‹€.

class Person {
    static let shared = Person()
    private var name: String = "KT"
    private var nickName: String = "Nury"
    private var age: Int = 50
    
    private init() { }
}

μ΄λ ‡κ²Œμš”! 그러면 μš°λ¦¬κ°€ μ›ν•˜λŠ”λŒ€λ‘œ νŠΉμ • λͺ©μ (μ‚¬μš©μž 정보)둜 μ•±μ—μ„œ μ‚¬μš©ν•  μœ μΌν•œ 객체가 λλ„€μš”~!

쑰아써 끝~~~!!!

 

이 μ•„λ‹ˆλΌ! μ—¬κΈ°μ„œ κΌ­ μ£Όμ˜ν•  점이 μžˆμ–΄μš”. λ°”λ‘œ thread-safeν•˜κ²Œ ꡬ성을 ν•΄μ€˜μ•Ό ν•œλ‹€λŠ” μ μΈλ°μš”!

응? thread-safeκ°€ 뭐야~~~??

 

ClassλŠ” Reference νƒ€μž…μ΄κΈ° λ•Œλ¬Έμ—, μ–΄λ””μ„œλ“  이 싱글톀 객체에 접근을 ν•˜κ²Œ 되면 νž™λ©”λͺ¨λ¦¬ μ˜μ—­μ— μžˆλŠ” 객체의 "원본데이터"에 μ ‘κ·Όν•˜κ²Œ λ˜μš”. 즉 그말은, μ•±μ˜ μ–΄λ–€κ³³μ—μ„œλŠ” λ‹‰λ„€μž„μ„ λ°”κΎΈλ €κ³  μ‹œλ„ν•˜κ³ , μ•±μ˜ μ–΄λ–€ κ³³μ—μ„œλŠ” λ‹‰λ„€μž„μ„ κ°€μ Έμ™€μ„œ λ³΄μ—¬μ£ΌκΈ°λ§Œ ν•  μˆ˜λ„ 있겠죠. 그럼.. λ‘˜μ€‘ ν•˜λ‚˜μ—μ„œλŠ” 잘λͺ»λœ λ™μž‘μ„ ν• κ±°μ—μš”! πŸ˜‚

 

즉, μš°λ¦¬λŠ” 싱글톀 객체λ₯Ό μ‚¬μš©ν•˜κ³  있고, 이 객체에 λŒ€ν•œ 데이터에 λŒ€ν•΄ μ—¬λŸ¬ threadμ—μ„œ λ™μ‹œμ— 접근을 ν•  μˆ˜λ„ μžˆμ„κ±°μ—μš”. μ΄λ ‡κ²Œ 되면 데이터가 κΌ¬μ—¬μ„œ, μ œλŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•Šκ² μ£ ? λ°”λ‘œ μ΄λ ‡κ²Œ μ—¬λŸ¬ threadμ—μ„œ λ™μ‹œμ— κ³΅μœ μžμ›μ„ μ ‘κ·Όν• λ•Œ, μ°Έμ‘°νƒ€μž…μ˜ 경우 데이터가 꼬일 수 μžˆλŠ”λ° 이런 μƒνƒœλ₯Ό "thread-unsafeν•˜λ‹€" 라고 ν•΄μš”.

그리고, 싱글톀 νŒ¨ν„΄μ—μ„œλŠ” Readers-Writers λ¬Έμ œλΌκ³ λ„ ν•΄μš”. (μ½λŠ” μ‚¬λžŒκ³Ό μ“°λŠ” μ‚¬λžŒμ˜ 문제! γ…‹γ…‹πŸ˜‚)

 

κ·ΈλŸ¬λ‹ˆκΉŒ μš°λ¦¬λŠ” 싱글톀 νŒ¨ν„΄μ„ μ‚¬μš©ν• λ•Œ, λ°˜λ“œμ‹œ thread-safeν•˜λ„λ‘ 섀계λ₯Ό ν•΄μ€˜μ•Όν•΄μš”.

이것도 예λ₯Ό λ“€μ–΄ λ³Όκ²Œμš”!

class Person {
    ...

    func changeNickname() {
        self.nickName = "changed Nury"
    }

    func getNickname() -> String {
        return self.nickName
    }
}

μ•±μ˜ μ–΄λ”˜κ°€μ—μ„œ λ‹‰λ„€μž„μ„ μˆ˜μ •ν•˜κ±°λ‚˜, μ½μ–΄μ˜€λŠ” 뢀뢄이 λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ λ™μ‹œμ— μˆ˜ν–‰λœλ‹€κ³  κ°€μ •ν•΄λ³Όκ²Œμš”.

그러면 λ‚˜λŠ” λ‹‰λ„€μž„μ„ "changed Nury"둜 λ³€κ²½ν–ˆλŠ”λ°, 화면에 λ³΄μ΄λŠ” λ‹‰λ„€μž„μ€ λ³€κ²½λ˜κΈ° μ „μ˜ κ·Έλƒ₯ "Nury" 일 수 μžˆλ‹€λŠ” λœ»μ΄μ—μš”.

이런 ν˜„μƒμ΄ λ°œμƒν•˜λŠ” 근본적인 μ΄μœ λŠ”, λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ "λ™μ‹œμ—" μˆ˜ν–‰λ˜κΈ° λ•Œλ¬ΈμΌκ±°μ—μš”.

 

음.. μ˜€μΌ€μ΄. 싱글톀 κ°μ²΄λŠ” κ³΅μœ μžμ›μ„ λ™μ‹œμ— μ ‘κ·Όν•  κ²½μš°μ— thread-unsafety ν•  수 μžˆκ΅¬λ‚˜.

그럼 이걸 thread-safe ν•˜λ„λ‘ μ–΄λ–»κ²Œ μ„€κ³„ν•΄μ€˜μ•Ό ν• κΉŒμš”? λ°”λ‘œλ°”λ‘œ... Concurrent Queueλ₯Ό ν™œμš©ν•˜λŠ” λ°©λ²•μ΄μ—μš”!

이것도 예λ₯Ό λ“€μ–΄ λ³Όκ²Œμš”!

class Person {
    ...
    private let nicknameQueue = DispatchQueue(label: "nickname queue", attributes: .concurrent)

    func changeNickname() {
        nicknameQueue.async(flags: .barrier) {
            self.nickName = "changed Nury"
        }
    }

    func getNickname() -> String {
        nicknameQueue.sync {
            return self.nickName
        }
    }
}

nicknameQueueλΌλŠ” Concurrent Queueλ₯Ό λ§Œλ“€μ–΄μ€¬μ–΄μš”.

쒀전에 이런 상황이 λ°œμƒν•˜λŠ” 근본적인 μ΄μœ λŠ” "λ™μ‹œμ—" λ‹€λ₯Έ μŠ€λ ˆλ“œμ—μ„œ κ³΅μœ μžμ›μ„ μ ‘κ·Όν•˜κΈ° λ•Œλ¬Έμ΄λΌκ³  ν–ˆμ—ˆμ£ ?

이걸 λ°©μ§€ν•˜κΈ° μœ„ν•΄, μ „μ²΄μ μœΌλ‘œλŠ” "λ™μ‹œμ—" μ ‘κ·Όν•˜λŠ” 것을 ν—ˆμš©ν•˜μ§€λ§Œ μ΄λ ‡κ²Œ Readers-Writers 상황에 봉착할 수 μžˆλŠ” μž‘μ—…μ€ λ”°λ‘œ serial(순차적으둜) 진행될 수 μžˆκ²Œλ” λ¬Άμ–΄μ£ΌλŠ” λ°©λ²•μœΌλ‘œ 이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” κ±°μ—μš”.

 

음.. nicknameQueueλ₯Ό μ •μ˜ν•΄μ€„λ•Œ concurrent둜 μ„ μ–Έν•΄μ€¬μœΌλ‹ˆ, λ™μ‹œμ— 접근을 ν—ˆμš©ν•œλ‹€λŠ”κ±° 아냐?

μˆœμ°¨μ μœΌλ‘œλŠ” μ–΄λ–»κ²Œ μ§„ν–‰ν•˜κ²Œλ” 할건데? μ΄λ•Œ λ°”λ‘œ queue의 flag에 barrierλ₯Ό μ‚¬μš©ν•΄μ£ΌλŠ” κ±°μ—μš”.

barrierλŠ” 말 κ·ΈλŒ€λ‘œ "방어막" 그런거죠~? 즉, barrier둜 감싸진 μž‘μ—…μ€ concurrent 큐 μ•ˆμ—μ„œλ„ Serial(순차적으둜) 진행될 수 μžˆκ²Œλ” λ§Œλ“€μ–΄μ£ΌλŠ” flagμ—μš”.

 

결둠적으둜 μ΄λ ‡κ²Œ 섀계λ₯Ό ν•΄λ†“μœΌλ©΄,

"응~ λ‹‰λ„€μž„ 변경에 λŒ€ν•œ μš”μ²­μ΄ 였면 κ·Έλƒ₯ λ³€κ²½ν•΄μ€˜! 단, λ‹‰λ„€μž„μ„ κ°€μ Έμ™€μ„œ λ³΄μ—¬μ€„λ•ŒλŠ” λ§Œμ•½ λ‹‰λ„€μž„μ„ λ³€κ²½ν•˜λŠ” μž‘μ—…μ΄ 진행쀑이면, κ·Έ μž‘μ—…μ΄ μ™„λ£Œλ˜κ³  μˆ˜ν–‰ν•΄μ€˜~!!!" κ°€ λ˜λŠ” κ±°μ—μš”.


μ‹€μ œλ‘œ iOSμ—μ„œ 기본적으둜 싱글톀 νŒ¨ν„΄μ— λŒ€ν•œ κΈ°λŠ₯을 μ§€μ›ν•˜κ³  μžˆλŠ”λ°μš”~!

let screen = UIScreen.main
let userDefault = UserDefaults.standard
let application = UIApplication.shared
let fileManager = FileManager.default
let notification = NotificationCenter.default

μ–΄λ””μ„œ 많이 λ³Έ κΈ°λŠ₯듀이고 μ‚¬μš©ν•˜κ³  κ³„μ‹œμ£ ~? 이런게 λͺ¨λ‘ μ‹±κΈ€ν†€μ΄λžλ‹ˆλ‹€~!! 

 

 

κ°„λ‹¨ν•œ μ˜ˆμ‹œλ₯Ό ν†΅ν•΄μ„œ λ””μžμΈ νŒ¨ν„΄μ€‘ ν•˜λ‚˜μΈ 싱글톀 νŒ¨ν„΄μ— λŒ€ν•΄μ„œ μ•Œμ•„λ³΄μ•˜μ–΄μš”!

μ–΄λ–»κ²Œ μ‚¬μš©ν•΄μ•Ό ν•˜λŠ”μ§€ λŠλ‚Œ μ’€ μ˜€μ‹œλ‚˜μš”~~? 😎

 

μ˜€λŠ˜λ„ λˆ„κ΅°κ°€μ—κ²Œ 도움이 λ˜μ—ˆκΈΈ λ°”λž˜μš” πŸ™

 

 

'iOS [Swift] > 기초λ₯Ό νƒ„νƒ„νžˆ!' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

[RxSwift] RxSwift μž…λ¬Έν•˜κΈ°  (4) 2021.03.30
[Swift] Class vs Struct  (0) 2021.03.16
[Swift] FileManager  (0) 2021.03.02
[Swift] Audio Recording  (0) 2021.03.01
[Swift] CaseIterable  (0) 2021.02.27
Comments