[swift] Swift에서 dispatch_once 싱글 톤 모델 사용

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 올바르게 정의 .



답변

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 프로젝트를 참조하십시오 .


답변

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를 사용하는 멋진 방법을 사용할 수 있습니다. 초기화 자로 전역 변수를 선언하고 전용으로 표시하십시오.

에서 여기


답변

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에 무료로 연결됩니다.


답변

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

class MySingleton {

    static let sharedMySingleton = MySingleton()

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


답변

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

var tpScopeManagerSharedInstance = TPScopeManager()

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

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

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


답변

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

class MyClass {

    private static let _sharedInstance = MyClass()

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

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


답변

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

class Singleton {
    static let sharedInstance = Singleton()
}

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

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

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