- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章深入理解Swift中单例模式的替换及Swift 3.0单例模式的实现由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
除了 MVC、MVVM 之外,单例模式可以说是 iOS 开发中另一常见的设计模式。无论是 UIKit 或是一些流行的三方库,我们都能看到单例的身影。而我们开发者本身也会潜意识地将这些类库中的代码当作最佳实践并将其带入日常工作中,哪怕很多人都知道单例存在一些明显的缺陷.
针对单例的缺陷,本文将介绍一些替换或改造单例模式的方法来提升代码质量.
单例的优点 。
除了上面提到的模仿最佳实践之外,单例的流行肯定也有内在的原因和理由。例如:单例对象保证了只有一个实例的存在,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。 另一方面,全局单一对象也减少了不必要的对象创建和销毁动作提高了效率.
下面是一个典型的单例模式代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
class
UserManager {
static
let shared = UserManager()
private
init() {
// 单例模式,防止出现多个实例
}
....
}
extension UserManager {
func logOut( ) {
...
}
func logIn( ) {
...
}
}
class
ProfileViewController: UIViewController {
private
lazy var nameLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = UserManager.shared.currentUser?.name
}
private
func handleLogOutButtonTap() {
UserManager.shared.logOut()
}
}
|
单例的缺陷 。
虽然上面提到了单例的一些优点,但是这不能掩盖单例模式一些明显的缺陷:
依赖注入 。
与之间之间使用单例对象不同,这里我们可以在初始化是进行依赖注入.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
class
ProfileViewController: UIViewController {
private
let user: User
private
let logOutService: LogOutService
private
lazy var nameLabel = UILabel()
init(user: User, logOutService: LogOutService) {
self.user = user
self.logOutService = logOutService
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = user.name
}
private
func handleLogOutButtonTap() {
logOutService.logOut()
}
}
class
LogOutService {
private
let user: User
private
let networkService: NetworkService
private
let navigationService: NavigationService
init(user: User,
networkService: NetworkService,
navigationService: NavigationService) {
self.user = user
self.networkService = networkService
self.navigationService = navigationService
}
func logOut() {
networkService.request(.logout(user)) { [weak self] in
self?.navigationService.showLoginScreen()
}
}
}
|
上面代码中的依赖关系明显比之前更为清晰,而且也更方便后期维护和编写测试实例。另外,通过 LogOutService 对象我们将某些特定服务抽离了出来,避免了单例中常见的臃肿状态.
协议化改造 。
将一个单例滥用的应用一次性全面改写为上面那样的依赖注入和服务化显然是一件非常耗时且不合理的事情。所以下面将会介绍通过协议对单例进行逐步改造的方法,这里主要的做法就是将上面 LogOutService 提供的服务改写为协议:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
protocol LogOutService {
func logOut()
}
protocol NetworkService {
func request(_ endpoint: Endpoint, completionHandler: @escaping () -> Void)
}
protocol NavigationService {
func showLoginScreen()
func showProfile(
for
user: User)
...
}
|
定义好协议服务之后,我们让原有的单例遵循该协议。此时我们可以在不修改原有代码实现的同时将单例对象当作服务进行依赖注入.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
extension UserManager: LoginService, LogOutService {}
extension AppDelegate: NavigationService {
func showLoginScreen() {
navigationController.viewControllers = [
LoginViewController(
loginService: UserManager.shared,
navigationService: self
)
]
}
func showProfile(
for
user: User) {
let viewController = ProfileViewController(
user: user,
logOutService: UserManager.shared
)
navigationController.pushViewController(viewController, animated:
true
)
}
}
|
Swift3.0 单例模式实现的几种方法-Dispatch_Once 。
在开发中需要使用单例模式是再寻常不过的了,正常我们的思路是使用GCD的dispatch_once这个API来写,然而在swift3.0中,苹果已经废弃了这个方法,不过不用担心,我们可以用别的方式来实现.
结合swift语言的特性,总结了以下几种写法
注意:这里我希望大家除了使用还要会调用该对应的方法 。
1.普通创建法 。
1
2
|
//MARK - : 单例:方法1
static
let shareSingleOne = Single()
|
2.静态创建法 。
1
2
3
4
5
6
7
|
let single = Single()
class
Single: NSObject {
//-MARK: 单例:方法2
class
var sharedInstance2 : Single {
return
single
}
}
|
3.struct创建法 。
1
2
3
4
5
6
7
|
//-MARK: 单例:方法3
static
var shareInstance3:Single{
struct
MyStatic{
static
var instance :Single = Single()
}
return
MyStatic.instance;
}
|
4.通过给DispatchQueue添加扩展实现 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
extension DispatchQueue {
private
static
var _onceTracker = [String]()
/**
Executes a block of code, associated with a unique token, only once. The code is thread safe and will
only execute the code once even in the presence of multithreaded calls.
- parameter token: A unique reverse DNS style name such as com.vectorform.<name> or a GUID
- parameter block: Block to execute once
*/
public
class
func once(token: String, block:()->Void) {
objc_sync_enter(self)
defer { objc_sync_exit(self) }
if
_onceTracker.contains(token) {
return
}
_onceTracker.append(token)
block()
}
}
|
使用字符串token作为once的ID,执行once的时候加了一个锁,避免多线程下的token判断不准确的问题.
使用的时候可以传token 。
1
2
3
|
DispatchQueue.once(token:
"com.vectorform.test"
) {
print(
"Do This Once!"
)
}
|
或者使用UUID也可以:
1
2
3
4
5
|
private
let _onceToken = NSUUID().uuidString
DispatchQueue.once(token: _onceToken) {
print(
"Do This Once!"
)
}
|
结语 。
单例模式并不是毫无可取之处,例如在日志服务、外设管理等场景下还是非常适用的。但是大多数时候单例模式由于依赖关系不明确以及全局共享可变状态可能会增加系统的复杂度造成一系列未知问题。如果你当前的代码中使用了大量的单例模式的话,我希望本文能够帮你从中解脱出来构建一个更健壮的系统.
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:https://bignerdcoding.com/archives/57.html 。
最后此篇关于深入理解Swift中单例模式的替换及Swift 3.0单例模式的实现的文章就讲到这里了,如果你想了解更多关于深入理解Swift中单例模式的替换及Swift 3.0单例模式的实现的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有没有办法在 .swift 文件(编译成 .swift 模块)中声明函数,如下所示: 你好.swift func hello_world() { println("hello world")
我正在尝试使用 xmpp_messenger_ios 和 XMPPFramework 在 iOS 上执行 MUC 这是加入房间的代码。 func createOrJoinRoomOnXMPP()
我想在我的应用程序上创建一个 3D Touch 快捷方式,我已经完成了有关快捷方式本身的所有操作,它显示正确,带有文本和图标。 当我运行这个快捷方式时,我的应用程序崩溃了,因为 AppDelegate
我的代码如下: let assetTag = Expression("asset_tag") let query2 = mdm.select(mdm[assetTag],os, mac, lastRe
我的 swift 代码如下所示 Family.arrayTuple:[(String,String)]? = [] Family.arrayTupleStorage:String? Family.ar
这是我的 JSON,当我读取 ord 和 uniq 数据时出现错误 let response2 : [String: Any] = ["Response":["status":"SUCCESS","
我想将 swift 扩展文件移动到 swift 包中。但是,将文件移动到 swift 包后,我遇到了这种错误: "Type 'NSAttributedString' has no member 'ma
使用CocoaPods,我们可以设置以下配置: pod 'SourceModel', :configurations => ['Debug'] 有什么方法可以用 Swift Package Manag
我正在 Xcode 中开发一个 swift 项目。我将其称为主要项目。我大部分都在工作。我在日期选择器、日期范围和日期数学方面遇到了麻烦,因此我开始了另一个名为 StarEndDate 的项目,其中只
这是 ObjectiveC 代码: CCSprite *progress = [CCSprite spriteWithImageNamed:@"progress.png"]; mProgressBar
我正在创建一个命令行工具,在 Xcode 中使用 Swift。我想使用一个类似于 grunt 的配置文件确实如此,但我希望它是像 Swift 包管理器的 package.swift 文件那样的快速代码
我假设这意味着使用系统上安装的任何 swift 运行脚本:#!/usr/bin/swift 如何指定脚本适用的解释器版本? 最佳答案 Cato可用于此: #!/usr/bin/env cato 1.2
代码说完全没问题,没有错误,但是当我去运行模拟器的时候,会出现这样的字样: (Swift.LazyMapCollection (_base:[ ] 我正在尝试创建一个显示报价的报价应用。 这是导入
是否可以在运行 Swift(例如 Perfect、Vapor、Kitura 等)的服务器上使用 RealmSwift 并使用它来存储数据? (我正在考虑尝试将其作为另一种解决方案的替代方案,例如 no
我刚开始学习编程,正在尝试完成 Swift 编程书中的实验。 它要求““编写一个函数,通过比较两个 Rank 值的原始值来比较它们。” enum Rank: Int { case Ace = 1 ca
在您将此问题标记为重复之前,我检查了 this question 它对我不起作用。 如何修复这个错误: error: SWIFT_VERSION '5.0' is unsupported, suppo
从 Xcode 9.3 开始,我在我的模型中使用“Swift.ImplicitlyUnwrappedOptional.some”包裹了我的字符串变量 我不知道这是怎么发生的,但它毁了我的应用程序! 我
这个问题在这里已经有了答案: How to include .swift file from other .swift file in an immediate mode? (2 个答案) 关闭 6
我正在使用 Swift Package Manager 创建一个应用程序,我需要知道构建项目的配置,即 Debug 或 Release。我试图避免使用 .xcodeproj 文件。请有人让我知道这是否
有一个带有函数定义的文件bar.swift: func bar() { println("bar") } 以及一个以立即模式运行的脚本foo.swift: #!/usr/bin/xcrun s
我是一名优秀的程序员,十分优秀!