如何使Swift Class Singleton实例线程安全?

时间:2017-04-13 13:26:50

标签: ios swift swift3 singleton

我有一个单身类:

class Database {
   static let instance:Database = Database()
   private var db: Connection?

   private init(){
      do {
        db = try Connection("\(path)/SalesPresenterDatabase.sqlite3")
        }catch{print(error)}
   }
}

现在我使用Database.instance.xxxxxx访问此类以在类中执行函数。但是,当我从另一个线程访问该实例时,它会抛出奇怪的结果,就像它试图创建另一个实例一样。我应该在同一个线程中引用该实例吗?

澄清奇怪的结果显示数据库I / O错误,因为两个实例一次尝试访问数据库

更新
有关数据库代码的更多信息,请参阅此问题:Using transactions to insert is throwing errors Sqlite.swift

3 个答案:

答案 0 :(得分:5)

class var shareInstance: ClassName {

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

USE:let object:ClassName = ClassName.shareInstance

Swift 3.0

class ClassName {
  static let sharedInstance: ClassName = { ClassName()} ()
}

USE:let object:ClassName = ClassName.shareInstance

答案 1 :(得分:3)

在Swift 3.0中添加一个私有init以防止其他人使用default()初始化程序。

class ClassName {
  static let sharedInstance = ClassName()
  private init() {} //This prevents others from using the default '()' initializer for this class.
}

答案 2 :(得分:0)

单线程类。

final public class SettingsThreadSafe {

   public static let shared = SettingsThreadSafe()

   private let concurrentQueue = DispatchQueue(label: "com.appname.typeOfQueueAndUse", attributes: .concurrent)
   private var settings: [String: Any] = ["Theme": "Dark",
                                       "MaxConsurrentDownloads": 4]

   private init() {}

   public func string(forKey key: String) -> String? {
       var result: String?
       concurrentQueue.sync {
           result = self.settings[key] as? String
       }
       return result
   }

   public func int(forKey key: String) -> Int? {
       var result: Int?
       concurrentQueue.sync {
           result = self.settings[key] as? Int
       }
       return result
   }

   public func set(value: Any, forKey key: String) {
       concurrentQueue.async( flags: .barrier ) {
           self.settings[key] = value
       }
   }
}

用于测试单例类的单元。

func testConcurrentUsage() {
    let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
    
    let expect = expectation(description: "Using SettingsThreadSafe.shared from multiple threads shall succeed")
    
    let callCount = 300
    for callIndex in 1...callCount {
        concurrentQueue.async {
            SettingsThreadSafe.shared.set(value: callIndex, forKey: String(callIndex))
        }
    }
    
    while SettingsThreadSafe.shared.int(forKey: String(callCount)) != callCount {
        // nop
    }
    
    expect.fulfill()
    waitForExpectations(timeout: 5) { (error) in
        XCTAssertNil(error, "Test expectation failed")
    }
}