您的位置:首页 > 移动开发 > Swift

Swift----单列的正确编写方式

2015-12-08 11:14 471 查看
本人每天早上都会利用坐地铁的时间去看CocoaChinade的公众号,确实会学到很多有用的知识。今天早上看到上面发布了一篇文章讲的是在Swift语言中单例到底应该如何编写。感觉写的很好 赶紧mark了一下,然后觉得应该用自己的理解写到自己的博客里面。废话不多说了,下面正文。

熟悉iOS开发的人都知道,在iOS开发中 状态管理会让人很痛苦,但是又不可避免,单例就是其中的一个例子。而在Swift中实现单例有很多种方式。下面 本菜借鉴大神的总结来给大家一一介绍一下。

首先 先看一下 Objective-C中实现单例的方式

@interface Kraken : NSObject
@end

@implementation Kraken

+ (instancetype)sharedInstance {
static Kraken *sharedInstance = nil;
static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
sharedInstance = [[Kraken alloc] init];
});
return sharedInstance;
}

@end


在上面这段代码中 我们可以看到单例的基本结构,接着让我们约定一些规则来更好的理解一下单列。

单列规则

关于单例 要牢记三个重要的准则

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

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

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

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

Swift单例

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

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.结构体方法(新瓶装老酒)

class TheOneAndOnlyKraken {
class var sharedInstance: TheOneAndOnlyKraken {
struct Static {
static let instance = TheOneAndOnlyKraken()
}
return Static.instance
}
}


这虽然比OC移植版本好了一些,但是还不够好。

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

private let sharedKraken = TheOneAndOnlyKraken()
class TheOneAndOnlyKraken {
class var sharedInstance: TheOneAndOnlyKraken {
return sharedKraken
}
}


这个版本看起来更Swiftter一些了,在此你可能会有疑问,为什么看不到dispatch_once?因为根据“Apple Swift博客中”的说法。以上方法都自动满足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%安全的。但是对于静态类变量来说,情况又如何?

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

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

class TheOneAndOnlyKraken {
static let sharedInstance = TheOneAndOnlyKraken()
}


以下是大神们提供的–证据!来看看截图:



使用全局单例方法



第一张图片展示了使用全局实例时的stack trace。标红的地方需要注意。在调用Kraken单例之前,先调用了swift_once,接下来是swift_once_block_invoke。Apple之前在文档中已经说过,“懒实例化”的全局变量会被自动放在dispatch_once块中,我们可以假定说的就是这个东西。

了解了这些知识,我们来看看漂亮的单行单例方法。如图所示,调用完全一样。这样,我们就有了证据证明单行单例方法是正确的。

不要忘记设置初始化方法为私有

@davedelong,Apple的Framework传道者,善意地提醒我:必须保证init方法的私有性,只有这样,才能保证单例是真正唯一的,避免外部对象通过访问init方法创建单例类的其他实例。由于Swift中的所有对象都是由公共的初始化方法创建的,我们需要重写自己的init方法,并设置其为私有的。这很简单,而且不会破坏到我们优雅的单行单例方法。

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


这样做就可以保证编译器在某个类尝试使用()来初始化TheOneAndOnlyKraken时,抛出错误:



就这样 单行单例, 完美!(金星老师手势)。

原文链接:http://www.cocoachina.com/swift/20151207/14584.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: