原文地址: 点击打开链接
最近抽风 粗略撸了几天的swift基础资料, 新项目就用swift写的.基本上是解决一个问题又碰到一个问题. 走到哪儿卡到哪儿
关于单例OC没什么好说的 . 度娘搜了下swift的单例,无外乎抄来抄去. 当然讲的讲的也挺好. 这里搜到一遍最新swift的资料 , 大家看一下 . 希望我有时间 翻一下.
单例是啥以及如何在swift中使用单例
软件开发中单例使用的非常广泛.尽管它很流行,一般还是反对使用它. 为啥呢? 在这个文章中,我将解释什么事单例以及如何使用
文章支持到Swift4 , Xcode9
什么是单例
单例很好理解.单例模式就是保证类的实例对象只有一个. 很简单,对吧?
单例要保证实例对象只有一个
如果你用过苹果的框架,那你肯定有可能已经用到单例了.看看下面的栗子,是不是很熟悉
// Shared URL Session
let sharedURLSession = URLSession.shared
// Default File Manager
let defaultFileManager = FileManager.default
// Standard User Defaults
let standardUserDefaults = UserDefaults.standard
// Default Payment Queue
let defaultPaymentQueue = SKPaymentQueue.default()
单例模式非常有用,你肯定有过多次想要确定某个类只实例化了一次,而且你用到的就是那个对象. 恰好这就是单例最基本且唯一的目标.
Storekit framework 的默认支付队列就是一个很好的栗子. 应用程序永远都不应该创建 SKPaymentQueue 这个类的实例. 操作系统使用 StoreKit框架创建一个支付队列供你的APP使用.这个支付队列可以通过 SKPaymentQueue 这个类的 default() 方法获取到. 这真是单例模式的一个完美举例
全局获取
但是单例有一个副作用也是我们经常选择使用单例的原因: 全局获取 (全局获取 某一个对象)
不过对单例而言 全局访问单例对象不过是单例模式的一点点副作用罢了.
不幸的是,很多开发人员在工程里面很随意的通过单例获取对象,(不是说的我吧?). 默认的支付队列可以通过 default() 获取.这就意味着 你可以在项目的任何地方或者这个队列.确实很方便,不过是要付出代价的
如果你想知道关于更过的单利模式的缺点,我建议你读一下这篇文章单利怎么坏了就 这篇文章有详细的说明 (没有翻译)
swift中如何使用单例
这里我将展示两个方法介绍如何在swift中使用单例. 第一个方法就不要用了, 我只是想用它说明一下swift语法的概念.
全局变量
最简单的创建单例的方法就是创建一个 全局变量
let sharedNetworkManager = NetworkManager(baseURL: API.baseURL)
class NetworkManager {
// MARK: - Properties
let baseURL: URL
// Initialization
init(baseURL: URL) {
self.baseURL = baseURL
}
}
通过在全局变量命名区创建一个变量, 整个项目的任何对象下 都可以获取这个单例对象了.举个例子 ,我们可以再AppDelegate中调用
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(sharedNetworkManager)
return true
}
在swift中全局变量都是懒加载实现的, 也就是说 只有第一次调用时,才会被引用.
swift 初始化有一个附加的好处就是自带dispath_once 的方法.这就保证了初始化方法只调用一次. 对你想要的单例模式而言这是很重要的
使用全局命名空间也有很多缺点.最大的缺点就是命名空间 太凌乱了.另外一个缺点就是NetWorkManager的初始化方法不能够被私有化,这个这个类就有可能在很多地方被实例化.我介绍一个更好的,也是我更喜欢的在swift中使用的方法.
Static Property and Private Initializer
swift 2 有了静态成员和权限访问设置. 这就有了更多的创建单利的方法. 比使用全局变量更简洁 更优雅. 话不多说看栗子
class NetworkManager {
// MARK: - Properties
static let shared = NetworkManager(baseURL: API.baseURL)
// MARK: -
let baseURL: URL
// Initialization
private init(baseURL: URL) {
self.baseURL = baseURL
}
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NetworkManager.shared)
return true
}
好几个实现的细节有所变化.首先init方法私有化,只有类自己内部才能创建自己的实例. 这是最明显的一个好处
其次, 我们声明了 shared 这个静态常量.这个属性给其他类一个访问NetworkManager的通道.
这就没有必要再给静态属性添加 lazy 关键字. 还记得前面说的 全局变量和静态成员默认都是懒加载的,这是另外一个好处
接下来的这个例子就有一点复杂了.主要区别就是单例在闭包中初始化.这样允许初始化的时候配置更多的东西.
class NetworkManager {
// MARK: - Properties
private static var sharedNetworkManager: NetworkManager = {
let networkManager = NetworkManager(baseURL: API.baseURL)
// Configuration
// ...
return networkManager
}()
// MARK: -
let baseURL: URL
// Initialization
private init(baseURL: URL) {
self.baseURL = baseURL
}
// MARK: - Accessors
class func shared() -> NetworkManager {
return sharedNetworkManager
}
}
私有声明这个属性,单例通过shared方法访问.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
print(NetworkManager.shared())
return true
}
cocoa里面的单例
通过上面的几个实现方法,我们已经模仿了cocoa 在swift3 中采用的好几个实现单例的方法.
// Shared URL Session
let sharedURLSession = URLSession.shared
// Default File Manager
let defaultFileManager = FileManager.default
// Standard User Defaults
let standardUserDefaults = UserDefaults.standard
// Default Payment Queue
let defaultPaymentQueue = SKPaymentQueue.default()
单例有毛病吗
在本篇的别处,我说了单例在项目中可能会有什么问题.我的建议就是能不用就不用.打算使用单例的时候,退一步看看有没有其他办法,是不是就必须要用单例?
尽管单例没有直接的自身问题.大多数开发者还是因为错误的原因采用它.因为方便啊,单例在任何地方都可以访问.