Swift设计模式(0) - 单例模式

作者: IAMDAEMON | 来源:发表于2016-11-08 22:49 被阅读84次

简单介绍单例模式

单例模式其实大家应该都耳熟能详了,至少在工作上会时不时的听到单例这个词。那么什么是单例模式,简单的讲,就是只有一个实例,可以在所有的地方调用。举个例子,你买了台PS4放在公司休闲区里,那么全公司的人,都可以去玩这台PS4,而且现实生活中的PS4并不能因为你new一个又多生成一个,那是没有意义的。
还有一个场景:有时候你想创建一个对象,并且让所有人都以一种简单一致的方式使用这个对象,这个时候用单例模式就会来的比较简单。举个例子:如果我们定义了一个logger.swift的类,这个类的作用是捕获项目里所有的log日志,如果不使用单例模式,那么如果创建了两个logger对象,这两个输出的log日志是独立的,并不能满足我们的捕捉所有日志的需求。这就是所谓的 封装共享资源

实现单例模式

实现单例模式必须遵循的原则:

  • 单例必须是该类唯一的实例
  • 单例不能被另一个对象取代,不管是谁,都!不!行!
  • 单例必须能让所有需要使用它的组件获取到

快速实现单例

在swift中可以通过使用 全部常量,快速实现单例模式。

let globalLogger = Logger()
final class Logger {
     private ini() {
        // do something
     }

   func log(msg: String) {
      // do something
   }
}

使用Swift常量可以保证两点:

  1. 全局常量是惰性初始化
  2. 这种惰性初始化是线程安全

final 关键字可以防止子类创建

传统地实现单例

final class Foo {
    ... //some var and func
    class var manager:Foo {
        struct SingletonWrapper {
            static let singleton = Foo(); 
        }
        return SingletonWrapper.singleton;
    }
}

为什么要嵌套结构体,因为Swift不支持类存储属性

处理并发

当我们使用单例的时候,往往就会出现一个情况,就是当有多个组件调用同一个单例的时候,会造成单例中的变量是线程不安全的。也就是说,不能同时的对一个类似数组等变量进行操作。所以最好使用 串行队列 来保证线程安全。

private let serialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
...
dispatch_sync(serialQueue, {() in
      // do something
})

可能遇到的问题

代码文件共享

在创建单例和定义全局常量时,应该用关键字修饰将他们定义在单独的文件中,这样其他组件就无法违反单例的原则。

不使用并发保护

如果应用对共享的数据结构存在依赖,例如对数组或者全局函数存在依赖,我们就应该确保单例代码不会同时被多个线程访问。如果不确定是否应该采取并发保护,那就采取并发保护。因为我们宁愿多消耗一点串行访问所需的成本,也不想让应用崩溃。

拙劣的优化

其实,当并发保护出现性能问题时,应该优先考虑一下代码的设计是否合理。而不是埋怨像GCD这样的并发机制性能不佳,一般GCD就够用了,而且GCD简单,容易理解。

Tips

barrier block

var arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_CONCURRENT)
...
dispatch_barrier_async(arrayQ, {() in
    // write data
})
...
dispatch_sync(arrayQ, {() in
    // read data
})

为什么要这样做呢?
这样做可以区分读取数组内容的线程和修改数组内容的线程
将读操作放在普通的block里,将写操作放在了barrier block里。dispatch_barrier_async会向队列丢一个block,并且同时会改变block的执行方式,上面这个队列会先看前面有没有其他任务要执行,如果有,就等着,等到在他面前所有的任务都执行完了,它再执行。
也就是说,当它到达了队列最前端时,GCD会等待所有正在进行的读操作完成,再进行写操作
再换句话说,使用barrier block会将并发队列暂时变成串行队列。这可以很方便的创建读/写锁

保护回调

上面的代码还有一个小问题,就是:如果我们在线程block里再丢一个block进去,like this:

var callback() -> Void
var arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_CONCURRENT)

init(callback -> Void) {
    self.callback = callback
}
...
dispatch_barrier_async(arrayQ, {() in
    // write data
    self.callBack()
})
...
dispatch_sync(arrayQ, {() in
    // read data
    self.callback()
})

这里的callback也是很有可能被并发调用的,所以我们可以采取比较合适的做法,就是让组件在提供callback的时候自行说明是否这个回调block加了保护。

var callback() -> Void
var arrayQ = dispatch_queue_create("arrayQ", DISPATCH_QUEUE_CONCURRENT)
var callbackQ =  dispatch_queue_create("callbackQ", DISPATCH_QUEUE_SERIAL)

init(callback -> Void, protect:Bool = true) {
    self.callback = callback
    if protect {
        // 如果添加保护,则加入到串行队列中
        self.callback = {() in 
              dispatch_sync(self.callbackQ, {() in
                    callback()
              })
        }
    }
}
...
dispatch_barrier_async(arrayQ, {() in
    // write data
    self.callBack()
})
...
dispatch_sync(arrayQ, {() in
    // read data
    self.callback()
})

相关文章

  • 设计模式(Swift) - 单例模式、备忘录模式和策略模式

    设计模式(Swift) - 单例模式、备忘录模式和策略模式 设计模式(Swift) - 单例模式、备忘录模式和策略模式

  • iOS-单例模式

    swift的单例设计模式 OC的单例设计模式 新学习一种单例思想

  • 设计模式(Swift) - 3.观察者模式、建造者模式

    上一篇 设计模式(Swift) - 2.单例模式、备忘录模式和策略模式中讲了三种常见的设计模式. 单例模式: 限制...

  • 一行代码实现swift的单例模式

    swift实现单例的四种方式 : 单例模式 单例模式是设计模式中最简单的一种,甚至有些模式大师都不称其为模式,称其...

  • Swift设计模式(0) - 单例模式

    简单介绍单例模式 单例模式其实大家应该都耳熟能详了,至少在工作上会时不时的听到单例这个词。那么什么是单例模式,简单...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

网友评论

    本文标题:Swift设计模式(0) - 单例模式

    本文链接:https://www.haomeiwen.com/subject/vxkzuttx.html