在 Swift 中实现单例方法

swift 发布于 2017年11月20日

从 Objective-C 的思路上来看,这样好像没有什么问题。但在 Swift 中,那么问题就来了。因为 Swift 中是不支持静态成员变量的,所以这行代码 class var instance: DBManager? 会产生编译错误。

{% img figures /images/singletion_1.jpg %}

那么就是说,在 Swift 中我们不能通过静态成员变量的方式实现单例方法了。那么还有什么其他的办法呢。我们还可以将实例变量的定义放到类的外面,比如这样:

var instance: DBManager?

class DBManager {
  class func sharedInstance() -> DBManager {
    if !instance {
      instance = DBManager()
    }

    return instance!
  }
}

这种方式是可以实现单例的,但会有一个问题。就是我们可以在任何文件中访问到 instance 这个变量,这样就很容易造成对这个实例变量的意外操作,并且这也不符合面向对象思想中的封装特性。

那么,还有什么方法呢?

Swift 中还有一种属性叫做 Computed 属性,而这种属性是可以定义成为静态类型的。 比如这样:

class DBManager {
  class var sharedInstance: DBManager {
    // return the single instance
  }
}

但是这个属性又有一个问题,就是它的值每次都需要经过计算生成的,也就是上面代码中大括号内的部分。所以它不能单独作为实例变量存放的地方,它只能够作为访问的入口。我们还需要一个地方来存储实例变量。

而我们又知道,Swift 的类定义中,又不能存在静态成员变量,而且把这个实例变量定义在类的外面又会失去封装性。那还有什么办法呢? 真急人。

还好,经过我们的仔细探索,发现了这么一个宝藏。那就是 struct 结构。虽然 class 定义中是不允许使用静态成员变量的,但是 struct 是可以的。比如我们可以这样写:

 struct Private {
      static var instance: DBManager?
}

这个struct结构是可以顺利编译通过的。我们接下来要做的就是将它和 DBManager 类结合起来:

class DBManager {
  class var sharedInstance: DBManager {
    struct Private {
      static var instance: DBManager?
    }

    if !Private.instance {
      Private.instance = DBManager()
    }

    return Private.instance!
  }
}

这样,我们的单例就基本完成了,把 struct 的定义放到了属性方法的里面,这样从类的外面就不能访问到它,保证了类的封装性。

基本很完美了,那么接下来,我们还要考虑最后一个问题,就是 线程安全,其实做起来也很简单,就是添加以下 dispatch_once 的调用。相信熟悉 Objective-C 开发的同学对这个方法并不陌生。

那么最后,我们完成后的单例模式定义如下:

class DBManager {
  class var sharedInstance: DBManager {
    struct Private {
      static var instance: DBManager?
      static var token: dispatch_once_t = 0
    }

    dispatch_once(&Private.token) {
      Private.instance = DBManager()
    }

    return Private.instance!
  }
}

如果你觉得这篇文章有帮助,还可以关注微信公众号 swift-cafe,会有更多我的原创内容分享给你~

本站文章均为原创内容,如需转载请注明出处,谢谢。
关注微信公众号
发现更多精彩
swift-cafe