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

简单介绍单例模式

单例模式其实大家应该都耳熟能详了,至少在工作上会时不时的听到单例这个词。那么什么是单例模式,简单的讲,就是只有一个实例,可以在所有的地方调用。举个例子,你买了台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()
})
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容