博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Swift中编写单例的正确方式
阅读量:5920 次
发布时间:2019-06-19

本文共 2876 字,大约阅读时间需要 9 分钟。

hot3.png

Swift是Objective-C的一种自然演变,它用如下的方式实现单例:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

 Kraken : NSObject

 

@implementation Kraken

 

+ (instancetype)sharedInstance {

    static Kraken *sharedInstance = nil;

    static dispatch_once_t onceToken;

     

    dispatch_once(&onceToken, ^{

        sharedInstance = [[Kraken alloc] init];

    });

    return sharedInstance;

}

 

在这个现成方案中,我们可以看到单例的基本结构。让我们来约定一些规则,这样便于更好的理解。

单例规则

关于单例,有三个重要的准则需要牢记:

1. 单例必须是唯一的(要不怎么叫单例?) 在程序生命周期中只能存在一个这样的实例。单例的存在使我们可以全局访问状态。例如:

NSNotificationCenter, UIApplication和NSUserDefaults。

2. 为保证单例的唯一性,单例类的初始化方法必须是私有的。这样就可以避免其他对象通过单例类创建额外的实例。

3. 考虑到规则1,为保证在整个程序的生命周期中值有一个实例被创建,单例必须是线程安全的。并发有时候确实挺复杂,简单说来,如果单例的代码不正确,如果有两个线程同时实例化一个单例对象,就可能会创建出两个单例对象。也就是说,必须保证单例的线程安全性,才可以保证其唯一性。通过调用dispatch_once,即可保证实例化代码只运行一次。

在程序中保持单例的唯一性,只初始化一次,这样并不难。帖子的余下部分中,需要记住:单例实现要满足隐藏的dispatch_once规则。

Swift单例

自Swift 1.0开始,创建单例有很多种方法。这些链接中已经有很详尽的描述,比如

,和

。但是谁喜欢点链接呢?先剧透一下吧:总共有4个版本。我们来清点一下:

1. 最丑陋方法(Swift皮,Objective-C心)

 

1

2

3

4

5

6

7

8

9

10

11

12

class TheOneAndOnlyKraken {

    class var sharedInstance: TheOneAndOnlyKraken {

        struct Static {

            static var onceToken: dispatch_once_t = 0

            static var instance: TheOneAndOnlyKraken? = nil

        }

        dispatch_once(&Static.onceToken) {

            Static.instance = TheOneAndOnlyKraken()

        }

        return Static.instance!

    }

}

这个版本是Objective-C的直接移植版。我认为它不好看是因为Swift本该更简洁、更有描述力。不要做个搬运工,要做就做的更好

2. 结构体方法(“新瓶装老酒)

 

1

2

3

4

5

6

7

8

class TheOneAndOnlyKraken {

    class var sharedInstance: TheOneAndOnlyKraken {

        struct Static {

            static let instance = TheOneAndOnlyKraken()

        }

        return Static.instance

    }

}

Swift 1.0时,不支持静态类变量,那时这个方法是不得已而为之。但使用结构体,就可以支持这个功能。因为静态变量的限制,我们被约束在这样的一个模型中。这比Objective-C移植版本好一些,但还不够好。有趣的是,在Swift 1.2发布几个月后,我还可以看到这种写法。在那之后,反而更多了。

3.全局变量方法(“单行单例”方法)

1

2

3

4

5

6

private let sharedKraken = TheOneAndOnlyKraken()

class TheOneAndOnlyKraken {

    class var sharedInstance: TheOneAndOnlyKraken {

        return sharedKraken

    }

}

在Swift 1.2以后,我们有了访问权限设置(access control specifiers) 的功能和静态类成员(static class members)。这意味着我们终于可以摆脱混乱的全局变量、全局命名空间,也不会发生命名空间冲突了。这个版本看起来更Swiftier一点。

现在,你可能会有疑问:为何看不到dispatch_once?根据的说法,以上方法都自动满足dispatch_once规则。这里有个帖子可以证明dispatch_once规则一直在起作用。

“全局变量(还有结构体和枚举体的静态成员)的Lazy初始化方法会在其被访问的时候调用一次。类似于调用'dispatch_once'以保证其初始化的原子性。这样就有了一种很酷的'单次调用'方式:只声明一个全局变量和私有的初始化方法即可。”--来自Apple's Swift Blog

“The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as `dispatch_once` to make sure that the initialization is atomic. This enables a cool way to use `dispatch_once` in your code: just declare a global variable with an initializer and mark it private.”)

这就是Apple官方文档给我们的所有信息,但这些已经足够证明全局变量和结构体/枚举体的静态成员是支持”dispatch_once”特性的。现在,我们相信使用全局变量来“懒包装”单例的初始化方法到dispatch_once代码块中是100%安全的。但是对于静态类变量来说,情况又如何?

这个问题带我们到更激动人心的思考中去:

正确的方法(也即是“单行单例法”)现在已经被证明正确。

 

1

2

3

class TheOneAndOnlyKraken {

    static let sharedInstance = TheOneAndOnlyKraken()

}

 

转载于:https://my.oschina.net/iceTear/blog/1585643

你可能感兴趣的文章
node环境下配置ES6在WebStorm中的执行环境
查看>>
2011年网络营销总结 缺少让人尖叫的案例
查看>>
Solaris 10u11 安装python2.7.10
查看>>
Office Web Apps证书的申请步骤讲解
查看>>
Global Azure上创建、配置、管理SQL Server信息
查看>>
常用端口号大全(详细)
查看>>
MySQL 8.0新特性--Descending Indexes(六)
查看>>
我的Android进阶之旅------>Android 关于arm64-v8a、armeabi-v7a、armeabi、x86下的so文件兼容问题...
查看>>
我的友情链接
查看>>
前端面试大全(四)
查看>>
IT人为什么难以拿到高薪?【转帖】
查看>>
工欲善其事必先利其器SecureCRT+VMware® Workstation_学习笔记
查看>>
Mac系统Git生成ssh公钥
查看>>
Runtime&json->model
查看>>
vim命令用法
查看>>
文件和目录权限chmod,更改所有者和所属组chown,umask,隐藏权限lsattr/chattr
查看>>
IOS精品源码,仿探探UIButton封装iOS提示弹框迅速引导页自定义导航栏
查看>>
阿里PB级Kubernetes日志平台建设实践
查看>>
springboot+redis项目实战
查看>>
消灭编译警告(Warning)
查看>>