Swift에서 dispatch_once 싱글 톤 모델 사용


575

Swift에서 사용하기 위해 적절한 싱글 톤 모델을 개발하려고합니다. 지금까지 스레드가 아닌 안전 모델을 다음과 같이 작동시킬 수있었습니다.

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
        }

        if !Static.instance {
            Static.instance = TPScopeManager()
        }

        return Static.instance!
    }
}

정적 구조체에서 싱글 톤 인스턴스를 래핑하면 복잡한 이름 지정 체계없이 싱글 톤 인스턴스와 충돌하지 않는 단일 인스턴스를 허용해야하며, 사물을 상당히 비공개로 만들어야합니다. 그러나이 모델은 스레드로부터 안전하지 않습니다. 그래서 나는 dispatch_once모든 것을 추가하려고했습니다 .

class var sharedInstance: TPScopeManager {
    get {
        struct Static {
            static var instance: TPScopeManager? = nil
            static var token: dispatch_once_t = 0
        }

        dispatch_once(Static.token) { Static.instance = TPScopeManager() }

        return Static.instance!
    }
}

그러나 나는 줄에 컴파일러 오류가 발생 dispatch_once합니다.

식의 'Void'형식을 '()'형식으로 변환 할 수 없습니다

구문의 여러 변형을 시도했지만 모두 동일한 결과가있는 것 같습니다.

dispatch_once(Static.token, { Static.instance = TPScopeManager() })

dispatch_onceSwift를 올바르게 사용하는 방법 은 무엇입니까 ? 나는 처음 ()에 오류 메시지 로 인해 문제가 블록에 있다고 생각 했지만 더 많이 볼수록 더 많은 문제가 발생할 수 있다고 생각합니다.dispatch_once_t 올바르게 정의 .


3
모든 정적 코드를 제거하고 @lazy 초기화 프로그램과 함께 읽기 전용 속성을 사용합니다.
Sulthan

1
그게 내 뜻이야 불행히도 우리는 여전히 내부 정보가 충분하지 않습니다. 그러나 IMHO의 구현은 @lazy스레드로부터 안전해야합니다.
Sulthan

1
또한이 방법은 구현을 호출자의 포식에 노출시키지 않는 이점이 있습니다.
David Berry

1
또한 @lazy 클래스 변수를 가질 수있는 것처럼 보이지 않습니다.
David Berry

조심해! 이 접근법에는 두 가지 사항이 있습니다. 먼저이 클래스에서 상속 된 모든 클래스는 sharedInstance 속성을 재정의해야합니다. Static.instance = TPScopeManager()인스턴스 유형을 강제합니다. Static.instance = self()필요한 이니셜 라이저와 같은 것을 사용 하면 적절한 유형 클래스가 생성됩니다. 그럼에도 불구하고 이것은 계층 구조의 모든 인스턴스에 대해 한 번만 유의해야 할 중요한 사항입니다! 초기화 할 첫 번째 유형은 모든 인스턴스에 대해 설정된 유형입니다. 나는 objective-c가 똑같이 행동했다고 생각하지 않습니다.
sean woodward

답변:


713

tl; dr : Swift 1.2 이상을 사용하는 경우 클래스 상수 접근법을 사용하고 이전 버전을 지원 해야하는 경우 중첩 구조 접근법을 사용하십시오.

Swift에 대한 경험에서 게으른 초기화 및 스레드 안전성을 지원하는 싱글 톤 패턴을 구현하는 세 가지 방법이 있습니다.

클래스 상수

class Singleton  {
   static let sharedInstance = Singleton()
}

이 방법은 Swift가 클래스 상수 (및 변수)를 지연 초기화하고의 정의에 의해 스레드로부터 안전하기 때문에 지연 초기화를 지원합니다 let. 이것은 이제 싱글 톤을 인스턴스화하기 위해 공식적으로 권장되는 방법 입니다.

클래스 상수는 Swift 1.2에 도입되었습니다. 이전 버전의 Swift를 지원해야하는 경우 아래의 중첩 된 구조체 접근 방식 또는 전역 상수를 사용하십시오.

중첩 된 구조체

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static let instance: Singleton = Singleton()
        }
        return Static.instance
    }
}

여기서는 중첩 구조체의 정적 상수를 클래스 상수로 사용합니다. 이것은 Swift 1.1 및 이전 버전에서 정적 클래스 상수가없는 경우의 해결 방법이며, 함수에 정적 상수 및 변수가없는 경우의 해결 방법으로 여전히 작동합니다.

dispatch_once

전통적인 Objective-C 접근 방식은 Swift로 이식되었습니다. 중첩 된 구조체 접근 방식에 비해 이점이 없다고 확신하지만 구문의 차이점이 흥미로워지면 여기에 배치합니다.

class Singleton {
    class var sharedInstance: Singleton {
        struct Static {
            static var onceToken: dispatch_once_t = 0
            static var instance: Singleton? = nil
        }
        dispatch_once(&Static.onceToken) {
            Static.instance = Singleton()
        }
        return Static.instance!
    }
}

단위 테스트는 이 GitHub 프로젝트를 참조하십시오 .


13
"let 덕분에 스레드 안전"—이 부분이 어디에 있습니까? 설명서에서 언급을 찾을 수 없습니다.
jtbandes 2016 년

4
@jtbandes 상수는 내가 알고있는 모든 언어에서 스레드 안전합니다.
hpique

2
@DaveWood 마지막 접근 방식에 대해 이야기한다고 가정합니다. "나는 더 이상이 방법을 사용할 필요는 없지만 구문의 차이점이 흥미로워지면 여기에 배치 할 것입니다."
hpique

5
해야 init또한 선언 할 private보장 하나와 객체의 하나의 인스턴스 만 지금까지 응용 프로그램의 수명 전반에 걸쳐 존재하는 것인가?
Andrew

5
"클래스 상수"접근 방식에서는 (a) 클래스를 선언하여 final서브 클래스가되지 않도록 선언 합니다. (b) 다른 인스턴스를 실수로 인스턴스화 할 수 없도록 init메소드를 선언합니다 private.
Rob

175

Apple은 정적 구조체 변수가 게으르고 래핑되어 초기화되었음을 dispatch_once알았 으므로 (포스트 끝의 참고 사항 참조) 최종 솔루션은 다음과 같습니다.

class WithSingleton {
    class var sharedInstance: WithSingleton {
        struct Singleton {
            static let instance = WithSingleton()
        }

        return Singleton.instance
    }
}

이것은 정적 구조체 요소의 자동 게으른 스레드 안전 초기화를 활용하고 소비자로부터 실제 구현을 안전하게 숨기고 가독성을 위해 모든 것을 컴팩트하게 구획화하며 보이는 전역 변수를 제거합니다.

애플은 게으른 이니셜 라이저가 스레드로부터 안전하다는 것을 분명히 했으므로 dispatch_once보호 가 필요하지 않습니다.

전역 변수에 대한 지연 초기화 프로그램 (struct 및 enum의 정적 멤버에 대해서도)은 전역에 처음 액세스 할 때 실행되며 dispatch_once로 실행되어 초기화가 원자적임을 확인합니다. 이렇게하면 코드에서 dispatch_once를 사용하는 멋진 방법을 사용할 수 있습니다. 초기화 자로 전역 변수를 선언하고 전용으로 표시하십시오.

에서 여기


1
확인 : 전역 변수에는 게으른 스레드 안전 초기화가 있지만 클래스 변수에는 없습니다. 권리?
Bill

14
private init() {}이 클래스를 외부 적으로 인스턴스화하지 않아야한다는 사실을 더욱 강화하기 위해 초기화 프로그램을 private :으로 선언하는 것이 좋습니다 .
Pascal Bourque

1
정적 구조체 var 초기화가 게으르고 스레드 안전합니다. 정적 구조체 var가 멀티 톤의 사전 인 경우 각 액세스에 대해 수동으로 호출을 동기화 / 큐해야합니까?

귀하의 질문을 올바르게 이해하면 사전 및 배열 액세스는 본질적으로 스레드 안전하지 않으므로 일부 유형의 스레드 동기화를 사용해야합니다.
David Berry

@DavidBerry이 싱글 톤 클래스 내에서 함수를 어떻게 호출해야합니까? myClass.sharedInstance의 첫 번째 호출에서 호출 할 함수가 필요합니다.
Ameet Dhas

163

Swift 1.2 이상 :

class Singleton  {
   static let sharedInstance = Singleton()
}

정확성의 증거와 함께 (모든 크레딧은 여기에 있습니다. ), 싱글 톤에 대해 이전 방법을 사용할 이유가 거의 없습니다.

업데이트 : 이것은 공식 문서에 설명 된대로 싱글 톤을 정의 하는 공식적인 방법입니다. !

staticvs 사용에 대한 우려 class. 변수 static를 사용할 수있는 경우에도 class사용할 수 있어야합니다. 싱글 톤은 기본 싱글 톤의 여러 인스턴스를 생성하기 때문에 서브 클래 싱되지 않습니다. 를 사용하면 static이것을 아름답고 깔끔하게 시행 할 수 있습니다.

Swift 1.0 및 1.1의 경우 :

최근 새로운 액세스 제어 방법 인 Swift의 최근 변경 사항으로 인해 이제 싱글 톤에 전역 변수를 사용하는 깔끔한 방법을 찾고 있습니다.

private let _singletonInstance = SingletonClass()
class SingletonClass {
  class var sharedInstance: SingletonClass {
    return _singletonInstance
  }
}

Swift 블로그 기사에서 언급했듯이 여기 :

전역 변수에 대한 지연 초기화 프로그램 (struct 및 enum의 정적 멤버에 대해서도)은 전역에 처음 액세스 할 때 실행되며 dispatch_once로 실행되어 초기화가 원자적임을 확인합니다. 이렇게하면 코드에서 dispatch_once를 사용하는 멋진 방법을 사용할 수 있습니다. 초기화 자로 전역 변수를 선언하고 전용으로 표시하십시오.

싱글 톤을 생성하는이 방법은 스레드 안전하고 빠르며 게으 르며 ObjC에 무료로 연결됩니다.


2
이 답변 만 읽는 사람 : 토큰을 정적으로 만들어야합니다. 그렇지 않으면 동작이 정의되지 않습니다. 전체 코드는 David의 편집 된 질문을 참조하십시오.
nschum 2016 년

@nschum 그렇지 않으면 동작이 정의되지 않고 잘 정의 된 방식으로 손상됩니다. 블록이 항상 실행됩니다.
Michael

@Michael : 문서에 정의되지 않았다고 나와 있습니다. 따라서 현재의 행동은 우연입니다.
nschum

1
말이 이상하다. 문서가 "정의되지 않음"이라고 부르면 코드를 작성한 사람이 그 일을 약속하지 않는다는 의미입니다. 변수가 정적인지 아는 코드와는 아무런 관련이 없습니다. 그것은 현재 (또는 명백한) 행동에 의존 할 수 없다는 것을 의미합니다.
nschum

6
private init() {}초기 자로 추가 할 수 있습니다 SingletonClass. 외부에서 인스턴스화를 방지합니다.
rintaro

46

Swift 1.2 이상은 이제 클래스에서 정적 변수 / 상수를 지원합니다. 따라서 정적 상수를 사용할 수 있습니다.

class MySingleton {

    static let sharedMySingleton = MySingleton()

    private init() {
        // ...
    }
}

35

더 좋은 방법이 있습니다. 클래스 선언 위에서 다음과 같이 클래스에서 전역 변수를 선언 할 수 있습니다.

var tpScopeManagerSharedInstance = TPScopeManager()

이것은 기본 init 또는 Sitft에 기본적으로 init 및 전역 변수가 dispatch_once있습니다. 그런 다음 어느 클래스에서든 참조를 얻으려면 다음과 같이하십시오.

var refrence = tpScopeManagerSharedInstance
// or you can just access properties and call methods directly
tpScopeManagerSharedInstance.someMethod()

따라서 기본적으로 공유 인스턴스 코드의 전체 블록을 제거 할 수 있습니다.


3
왜 "var"이고 많은 "let"입니까?
Stephan

1
아마 let 일 수 있습니다. 나는 var로 테스트했습니다.
Kris Gellci

나는이 답변을 좋아하지만 Interface Builder 에서이 (Singleton)에 액세스해야합니다. IB 내에서이 tpScopeManagerSharedInstance에 어떻게 액세스 할 수 있습니까? 감사합니다.
Luis Palacios

이것이 싱글 톤을 갖는 내가 선호하는 방법입니다. 모든 일반적인 기능 (스레드 안전성 및 지연 인스턴스화) 가지고 있으며 매우 가벼운 구문을 지원합니다. TPScopeManager.sharedInstance.doIt()항상 쓸 필요가 없으며 클래스 이름 TPScopeManagerClass을 지정하고 클래스 옆 에이 선언을 public let TPScopeManager = TPScopeManagerClass()사용하고 write 만 사용하면됩니다 TPScopeManager.doIt(). 매우 깨끗합니다!
Alex

의 추가 인스턴스 생성을 막을 TPScopeManager수있는 것은 없으므로 정의상 싱글 톤아닙니다 .
Caleb

28

스위프트의 싱글은 예를 들어, 클래스 함수와 같은 코코아 프레임 워크에 노출되어있다 NSFileManager.defaultManager(), NSNotificationCenter.defaultCenter(). 따라서 다른 솔루션과 같은 클래스 변수보다는이 동작을 미러링하는 클래스 함수로 더 적합합니다. 예 :

class MyClass {

    private static let _sharedInstance = MyClass()

    class func sharedInstance() -> MyClass {
        return _sharedInstance
    }
}

를 통해 싱글 톤을 검색하십시오 MyClass.sharedInstance().


1
LearnCocos2D :)의 의견과 스타일에 대해서도 찬성했습니다.
x4h1d

2
전역 변수는 클래스 내부의 정적을 통해 클래스 변수로 변경해야합니다.
malhal

2
변수가 private이지만 클래스 외부에 표시되면 @malhal 전역 적이 지 않지만 내부 파일에만 범위가 있습니다. 클래스 내부의 정적은 거의 동일하게 작동하지만 정적을 사용하도록 답변을 업데이트했습니다. 파일 내에서 여러 클래스를 사용하는 경우 변수를 클래스로 그룹화하는 것이 좋습니다.
라이언

1
"Swift Singletons는 클래스 함수로 코코아 프레임 워크에 노출됩니다"... Swift 3에는 없습니다 static. 일반적으로 속성입니다.
Rob

17

Apple 문서에 따르면 Swift에서 가장 쉬운 방법은 정적 유형 속성을 사용하는 것입니다.

class Singleton {
    static let sharedInstance = Singleton()
}

그러나 간단한 생성자 호출 이외의 추가 설정을 수행하는 방법을 찾고 있다면 비밀은 즉시 호출 된 클로저를 사용하는 것입니다.

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

이것은 스레드 안전하고 한 번만 게으르게 초기화되도록 보장됩니다.


정적 let 인스턴스를 다시 nil로 다시 설정하려면 어떻게해야합니까?
gpichler

1
@ user1463853-당신은 할 수 없으며 일반적으로해서는 안됩니다.
Rob

16

스위프트 4+

protocol Singleton: class {
    static var sharedInstance: Self { get }
}

final class Kraken: Singleton {
    static let sharedInstance = Kraken()
    private init() {}
}

2
이것은 내가 구조체와 싱글의 다른 솔루션과 문제가 사촌 당신이 더 많은 차이를 설명 할 수있는, 최종 클래스를 필요로
Raheel 디크

개인 재정의 여야합니다 init () {}
NSRover

8

Apple의 샘플 코드를 보면이 패턴을 발견했습니다. Swift가 정적을 처리하는 방법을 잘 모르겠지만 C #에서는 스레드 안전합니다. Objective-C interop의 속성과 방법을 모두 포함합니다.

struct StaticRank {
    static let shared = RankMapping()
}

class func sharedInstance() -> RankMapping {
    return StaticRank.shared
}

class var shared:RankMapping {
    return StaticRank.shared
}

이 기본 정적 구문을 사용하면 모든 성가신 작업을 수행 할 것이라고 확신합니다.
Eonil

불행히도 스태틱은 구조체 내부에서만 작동하므로 이런 패턴입니다.
user2485100

내 의도는 우리가 dispatch_once물건 을 사용할 필요가 없다는 것이었다 . 나는 당신의 스타일에 베팅하고 있습니다. :)
Eonil

class클래스 선언 내에 static구조체 선언 과 동일 하지 않습니까?
Russell Borogove 2016 년

@Sam 예. 에 애플 블로그 항목을 참조하십시오파일 및 초기화 전역 및 정적 구조체 및 열거 형 멤버 모두이 dispatch_once기능의 이점을 누릴 수 있습니다.
Rob

5

간단히 말해서

class Manager {
    static let sharedInstance = Manager()
    private init() {}
}

파일 및 초기화 를 읽을 수 있습니다

전역 변수 (구조 및 열거의 정적 멤버)에 대한 지연 초기화 프로그램은 전역에 처음 액세스 할 때 실행 dispatch_once되며 초기화가 원자인지 확인하기 위해 시작됩니다 .


4

Objective-C에서 Swift 싱글 톤 클래스를 사용할 계획이라면이 설정에서 컴파일러가 적절한 Objective-C와 같은 헤더를 생성하게됩니다.

class func sharedStore() -> ImageStore {
struct Static {
    static let instance : ImageStore = ImageStore()
    }
    return Static.instance
}

그런 다음 Objective-C 클래스에서 스위프트 전날에 싱글 톤을 호출 한 방식으로 호출 할 수 있습니다.

[ImageStore sharedStore];

이것은 단지 간단한 구현입니다.


이것은 다른 Swift 싱글 톤과 동일한 방식으로 구현되기 때문에 다른 예제보다 더 간결하고 정확합니다. 예 : 클래스 기능으로 좋아 NSFileManager.defaultManager()하지만, 여전히 스위프트의 게으른 스레드 안전 정적 멤버 메커니즘을 사용합니다.
Leslie Godwin 5

코코아는 일반적으로 이것을 클래스 함수가 ​​아닌 정적 속성으로 구현합니다.
Rob

나는 내 의견이 2 세 이상이라는 것을 알고 있습니다. 언급 해 주셔서 감사합니다.
Michael

4

첫 번째 해결책

let SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

나중에 코드에서 :

func someFunction() {        
    var socketManager = SocketManager        
}

두 번째 해결책

func SocketManager() -> SocketManagerSingleton {
    return _SocketManager
}
let _SocketManager = SocketManagerSingleton();

class SocketManagerSingleton {

}

그리고 나중에 코드에서 혼동을 줄이기 위해 중괄호를 유지할 수 있습니다.

func someFunction() {        
    var socketManager = SocketManager()        
}

4
final class MySingleton {
     private init() {}
     static let shared = MySingleton()
}

그런 다음 전화하십시오.

let shared = MySingleton.shared

음만을 표시하지 않는 일 initprivate뿐만 아니라 만들기위한 sharedMyModelfinal! 미래 독자들을 위해 Swift 3에서는 이름 sharedMyModel을 간단히 로 바꾸는 경향이 있습니다 shared.
Rob

super.init에 대한 재정의와 호출이 잘못되어 컴파일되지 않는 것을 제외하고는 정답입니다.
Michael Morris

4

사용하다:

class UtilSingleton: NSObject {

    var iVal: Int = 0

    class var shareInstance: UtilSingleton {
        get {
            struct Static {
                static var instance: UtilSingleton? = nil
                static var token: dispatch_once_t = 0
            }
            dispatch_once(&Static.token, {
                Static.instance = UtilSingleton()
            })
            return Static.instance!
        }
    }
}

사용하는 방법:

UtilSingleton.shareInstance.iVal++
println("singleton new iVal = \(UtilSingleton.shareInstance.iVal)")

이것은 현재 답변으로가는 과정에서 겪은 답변 중 하나와 정확히 동일합니다. 전역 변수는 지연 및 스레드 안전으로 초기화되므로 추가 복잡성에 대한 이유가 없습니다.
David Berry

@David 전역 변수가없는 것 이외. :)
hpique

@hpique 아니오, 이전의 시도 중 하나와 정확히 같습니다. 편집 히스토리를보십시오.
David Berry

4

1.2보다 빠른 Swift에서 가장 좋은 방법은 다음과 같이 한 줄짜리 싱글 톤입니다.

class Shared: NSObject {

    static let sharedInstance = Shared()

    private override init() { }
}

이 방법에 대한 자세한 내용을 보려면이 링크를 방문 하십시오 .


NSObject서브 클래스인가?. 그 외에도, 이것은 본질적으로 stackoverflow.com/a/28436202/1187415 와 동일한 것으로 보입니다 .
Martin R

3

Apple Docs (Swift 3.0.1)에서

정적 유형 속성을 사용하면 여러 스레드에서 동시에 액세스 할 때에도 한 번만 지연 초기화 될 수 있습니다.

class Singleton {
    static let sharedInstance = Singleton()
}

초기화 이외의 추가 설정을 수행해야하는 경우 클로저 호출 결과를 전역 상수에 지정할 수 있습니다.

class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code
        return instance
    }()
}

3

enumJava에서 사용 하는 것처럼을 제안합니다 .

enum SharedTPScopeManager: TPScopeManager {
    case Singleton
}

IMO, 이것이 싱글 톤을 구현하는 유일한 올바른 Swift 방법입니다. 다른 답변은 ObjC / C / C ++ 방식입니다
Bryan Chen

이 답변에 대해 자세히 설명해 주시겠습니까? 이 스 니펫에서 Singleton이 어디에서 인스턴스화되는지는 확실하지 않습니다.
Kenny Winker

@KennyWinker Apple 개발자 로그인이 없으므로 신속하지 않으므로 초기화가 발생하면 대답 할 수 없습니다. Java에서는 처음 사용 중입니다. 초기화시 프린트로 시도해 볼 수 있고 프린트가 시작시 또는 액세스 후 발생하는지 확인할 수 있습니다. 컴파일러가 enum을 구현하는 방법에 따라 다릅니다.
Howard Lovatt

@KennyWinkler : Apple은 이것이 어떻게 작동하는지 명확하게 설명 했습니다 . developer.apple.com/swift/blog/?id=7을 참조하십시오 . 여기에는 "자바와 유사하게 처음 참조 될 때 글로벌 초기화 프로그램을 실행하십시오"라고 명시되어 있습니다. 또한 덮개 아래에서 "초기화가 원 자성인지 확인하기 위해 dispatch_once"를 사용하고 있다고 말합니다. 따라서 enum은 멋진 초기화가 없다면 개인용 정적 렛트가 해결책이 아니라면 갈 것입니다.
Howard Lovatt

2

참고로 여기에 Jack Wu / hpique의 Nested Struct 구현의 Singleton 구현 예가 있습니다. 또한 구현 기능과 함께 제공되는 기능을 보여줍니다. 나는 이것이 완전한 예를 찾을 수 없었으므로 희망적으로 이것은 누군가를 돕는다!

import Foundation

class ItemStore: NSObject {

    class var sharedStore : ItemStore {
        struct Singleton {
            // lazily initiated, thread-safe from "let"
            static let instance = ItemStore()
        }
        return Singleton.instance
    }

    var _privateItems = Item[]()
    // The allItems property can't be changed by other objects
    var allItems: Item[] {
        return _privateItems
    }

    init() {
        super.init()
        let path = itemArchivePath
        // Returns "nil" if there is no file at the path
        let unarchivedItems : AnyObject! = NSKeyedUnarchiver.unarchiveObjectWithFile(path)

        // If there were archived items saved, set _privateItems for the shared store equal to that
        if unarchivedItems {
            _privateItems = unarchivedItems as Array<Item>
        } 

        delayOnMainQueueFor(numberOfSeconds: 0.1, action: {
            assert(self === ItemStore.sharedStore, "Only one instance of ItemStore allowed!")
        })
    }

    func createItem() -> Item {
        let item = Item.randomItem()
        _privateItems.append(item)
        return item
    }

    func removeItem(item: Item) {
        for (index, element) in enumerate(_privateItems) {
            if element === item {
                _privateItems.removeAtIndex(index)
                // Delete an items image from the image store when the item is 
                // getting deleted
                ImageStore.sharedStore.deleteImageForKey(item.itemKey)
            }
        }
    }

    func moveItemAtIndex(fromIndex: Int, toIndex: Int) {
        _privateItems.moveObjectAtIndex(fromIndex, toIndex: toIndex)
    }

    var itemArchivePath: String {
        // Create a filepath for archiving
        let documentDirectories = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
        // Get the one document directory from that list
        let documentDirectory = documentDirectories[0] as String
        // append with the items.archive file name, then return
        return documentDirectory.stringByAppendingPathComponent("items.archive")
    }

    func saveChanges() -> Bool {
        let path = itemArchivePath
        // Return "true" on success
        return NSKeyedArchiver.archiveRootObject(_privateItems, toFile: path)
    }
}

그리고 그 기능 중 일부를 인식하지 못하면 사용중인 Swift 유틸리티 파일이 있습니다.

import Foundation
import UIKit

typealias completionBlock = () -> ()

extension Array {
    func contains(#object:AnyObject) -> Bool {
        return self.bridgeToObjectiveC().containsObject(object)
    }

    func indexOf(#object:AnyObject) -> Int {
        return self.bridgeToObjectiveC().indexOfObject(object)
    }

    mutating func moveObjectAtIndex(fromIndex: Int, toIndex: Int) {
        if ((fromIndex == toIndex) || (fromIndex > self.count) ||
            (toIndex > self.count)) {
                return
        }
        // Get object being moved so it can be re-inserted
        let object = self[fromIndex]

        // Remove object from array
        self.removeAtIndex(fromIndex)

        // Insert object in array at new location
        self.insert(object, atIndex: toIndex)
    }
}

func delayOnMainQueueFor(numberOfSeconds delay:Double, action closure:()->()) {
    dispatch_after(
        dispatch_time(
            DISPATCH_TIME_NOW,
            Int64(delay * Double(NSEC_PER_SEC))
        ),
        dispatch_get_main_queue()) {
            closure()
    }
}

2

신속하게 다음과 같은 방법으로 싱글 톤 클래스를 만들 수 있습니다.

class AppSingleton: NSObject {

    //Shared instance of class
    static let sharedInstance = AppSingleton()

    override init() {
        super.init()
    }
}

1

나는이 구현을 선호한다 :

class APIClient {

}

var sharedAPIClient: APIClient = {
    return APIClient()
}()

extension APIClient {
    class func sharedClient() -> APIClient {
        return sharedAPIClient
    }
}

1

스위프트에서의 구현 방식은 ...

ConfigurationManager.swift

import Foundation

    let ConfigurationManagerSharedInstance = ConfigurationManager()
 class ConfigurationManager : NSObject {
    var globalDic: NSMutableDictionary = NSMutableDictionary()

class var sharedInstance:ConfigurationManager {
    return ConfigurationManagerSharedInstance

}

init() {

    super.init()

    println ("Config Init been Initiated, this will be called only onece irrespective of many calls")   

}

아래의 응용 프로그램 화면에서 globalDic에 액세스하십시오.

읽다:

 println(ConfigurationManager.sharedInstance.globalDic)  

쓰다:

 ConfigurationManager.sharedInstance.globalDic = tmpDic // tmpDict is any value that to be shared among the application

1

올바른 접근 방식은 다음과 같습니다.

final class Singleton {
    static let sharedInstance: Singleton = {
        let instance = Singleton()
        // setup code if anything
        return instance
    }()

    private init() {}
}

액세스

let signleton = Singleton.sharedInstance

원인:

  • static 여러 스레드에서 동시에 액세스 할 때도 type 속성이 한 번만 지연 초기화 될 수 있으므로 사용할 필요가 없습니다. dispatch_once
  • init다른 클래스에서 인스턴스를 작성할 수 없도록 메소드를 민영화하십시오 .
  • final 다른 클래스가 Singleton 클래스를 상속하지 않기를 원합니다.

직접 사용할 수있는 동안 클로저 초기화를 사용한 이유static let sharedInstance = Singleton()
abhimuralidharan

1
추가 설정을 원하지 않으면 원하는 말이 맞습니다.
applefreak

1

David의 구현을 본 후에 는 클래스 메서드 와 거의 동일한 작업을 수행 instanceMethod하므로 싱글 톤 클래스 함수가 ​​필요하지 않은 것 같습니다 . 전역 상수로 선언하기 만하면됩니다.letsharedInstance

let gScopeManagerSharedInstance = ScopeManager()

class ScopeManager {
   // No need for a class method to return the shared instance. Use the gScopeManagerSharedInstance directly. 
}

2
내 의견에서 말했듯이, 앞으로해야 할 유일한 이유는 미래의 어느 시점에서 전역 변수를 이동 / 숨기기하고 더 많은 싱글 톤과 같은 동작을 얻을 수 있기 때문입니다. 이때 모든 것이 일관된 패턴을 사용하는 경우 사용법을 변경하지 않고도 싱글 톤 클래스 자체를 변경할 수 있습니다.
David Berry

0
   func init() -> ClassA {
    struct Static {
        static var onceToken : dispatch_once_t = 0
        static var instance : ClassA? = nil
    }

    dispatch_once(&Static.onceToken) {
        Static.instance = ClassA()
    }

    return Static.instance!
}

여기에서 오랫동안 논의했듯이 dispatch_once정적 변수 초기화가 게으르고 dispatch_once Apple을 통해 자동으로 보호되므로 실제로는 그 이유 때문에 dispatch_once 대신 정적을 사용하는 것이 좋습니다.
David Berry

0

과거에 싱글 톤을 실현하는 스위프트는 전역 변수, 내부 변수 및 dispatch_once 방법의 세 가지 방법에 지나지 않습니다.

여기에 두 가지 좋은 싱글 톤이 있습니다 (참고 : 어떤 종류의 글을 쓰더라도 init () 방식의 사유화에주의를 기울여야합니다 .Swift에서는 모든 객체의 생성자 기본값이 공개이므로 다시 작성해야합니다. ,이 클래스 '()'의 다른 객체가 기본 초기화 메소드에 의해 객체를 작성하지 않도록하십시오.)

방법 1 :

class AppManager {
    private static let _sharedInstance = AppManager()

    class func getSharedInstance() -> AppManager {
       return _sharedInstance
    }

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.getSharedInstance()

방법 2 :

class AppManager {
    static let sharedInstance = AppManager()

    private init() {} // Privatizing the init method
}

// How to use?
AppManager.sharedInstance

-1

스레드 안전 기능이있는 가장 간단한 것입니다. 원하는 경우에도 다른 스레드가 동일한 단일 객체에 액세스 할 수 없습니다. 스위프트 3/4

struct DataService {

    private static var _instance : DataService?

    private init() {}   //cannot initialise from outer class

    public static var instance : DataService {
        get {
            if _instance == nil {
                DispatchQueue.global().sync(flags: .barrier) {
                    if _instance == nil {
                        _instance = DataService()
                    }
                }
            }
            return _instance!
        }
    }
}

2
정적 유형 속성의 장점은 무엇입니까 (여러 스레드에서 동시에 액세스 할 때도 한 번만 지연 초기화됩니다)?
Martin R

-1

상속을 허용하려면 싱글 톤이 필요했으며 실제로 이러한 솔루션 중 어느 것도 허용하지 않았습니다. 그래서 나는 이것을 생각해 냈습니다.

public class Singleton {
    private static var sharedInstanceVar = Singleton()

    public class func sharedInstance() -> Singleton {
        return sharedInstanceVar
    }
}


public class SubSingleton: Singleton {

    private static var sharedInstanceToken: dispatch_once_t = 0

    public class override func sharedInstance() -> SubSingleton {
        dispatch_once(&sharedInstanceToken) {
            sharedInstanceVar = SubSingleton()
        }
    return sharedInstanceVar as! SubSingleton
    }
}
  • 이런 식으로 Singleton.sharedInstance()처음 할 때 인스턴스를 반환합니다.Singleton
  • SubSingleton.sharedInstance()처음 할 때 인스턴스를 반환합니다SubSingleton 생성 된 .
  • 위의 작업이 완료되면, SubSingleton.sharedInstance()이다 Singletontrue이고 동일한 인스턴스가 사용된다.

이 첫 번째 더러운 접근 방식의 문제는 하위 클래스가 구현할 것을 보장 할 수 없다는 것 dispatch_once_t입니다.sharedInstanceVar 클래스 당 한 번만 수정 입니다.

나는 이것을 더 구체화하려고 노력할 것이지만, 누군가 그것에 대해 강한 감정을 가지고 있는지를 보는 것은 흥미로울 것입니다 (장황하고 수동으로 업데이트해야한다는 사실 외에도).



-2

다음 구문을 사용합니다.

public final class Singleton {    
    private class func sharedInstance() -> Singleton {
        struct Static {
            //Singleton instance.
            static let sharedInstance = Singleton()
        }
        return Static.sharedInstance
    }

    private init() { }

    class var instance: Singleton {
        return sharedInstance()
    }
}

이것은 Swift 1.2에서 최대 4까지 작동하며 몇 가지 장점이 있습니다.

  1. 사용자에게 구현을 서브 클래스하지 말라고 상기시킵니다.
  2. 추가 인스턴스 생성 방지
  3. 게으른 생성 및 고유 한 인스턴스화 보장
  4. 다음과 같이 인스턴스에 액세스 할 수 있도록하여 구문 (피해야 함 ())을 줄입니다. Singleton.instance
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.