Swift、Objective-C 单例模式 (Singleton)

本文的单例模式分为严格单例模式不严格单例模式。单例模式要求一个类有一个实例,有公开接口可以访问这个实例。严格单例模式,要求一个类只有一个实例;不严格单例模式,可以创建多个实例。

有的类只能有一个实例,例如 UIApplication,通过 shared 属性访问唯一的实例,属于严格单例模式。有用户登录功能的 App 中,如果当前用户的数据模型与其他用户的数据模型不同,那么当前用户的类也应该用严格单例模式。在逻辑上,当前用户只有一个,只能有一个实例;这样可以在各个地方访问当前用户的数据。如果当前用户的数据模型与其他用户的数据模型相同,则应用不严格单例模式。可以给其他用户创建实例,同时也可以在各个地方访问当前用户的数据。

Swift 实现

严格单例模式

大多数 Objective-C 的类都继承自 NSObject,而 Swift 的类可以继承自 NSObject 或者不继承。

继承自 NSObject

class SingletonClass: NSObject {

    static let shared = SingletonClass()

    // Make sure the class has only one instance
// Should not init or copy outside
private override init() {} override func copy() -> Any {
return self // SingletonClass.shared
} override func mutableCopy() -> Any {
return self // SingletonClass.shared
} // Optional
func reset() {
// Reset all properties to default value
}
}

静态属性 shared 持有唯一的实例,对外公开。

重载 init() 方法,使其对外不可见,不可以在外部调用,防止在外部创建实例。

重载 copy()、mutableCopy() 方法,返回 self,防止在外部复制实例。这里也可以返回 SingletonClass.shared,效果是一样的,因为只有一个实例。只有 shared 能调用 copy()、mutableCopy() 方法,那么 self 就是 shared。写 self,代码比较简洁。

单例一旦创建,一直持有,不能手动销毁,但可以重置数据。如果需要的话,可以添加一个重置数据的方法 reset()。例如,当前用户退出登录,需要把当前用户实例的所有属性重置为默认值,防止数据错误。

不继承自 NSObject

class SingletonClass2 {

    static let shared = SingletonClass2()

    // Make sure the class has only one instance
// Should not init outside
private init() {} // Optional
func reset() {
// Reset all properties to default value
}
}

不继承自 NSObject 的类没有 copy()、mutableCopy() 方法,不需要重载。其他同上。

不严格单例模式

把重载的 init() 方法去掉,或者把 private 去掉,即可创建多个实例。如果继承自 NSObject,重载 copy()、mutableCopy() 方法:创建新实例,传递数据给新实例,返回新实例。其他与严格单例模式相同。

不严谨的重置数据方法

如果单例有很多属性,重置数据需要把每个属性都变成默认值,则 reset() 方法要写很多。有一种不严谨的重置数据方法:重新生成一个实例并赋值给持有实例的静态变量。

class SingletonClass3 {

    private static var _shared = SingletonClass3()

    static var shared: SingletonClass3 {
return _shared
} private init() {} // Not safe
// We can obtain more than one instance outside with this function
func reset() {
SingletonClass3._shared = SingletonClass3()
}
}

如果在外部访问单例都通过 shared 属性,这么写不会出错。然而,如果外部持有单例,就有可能出错。

let s = SingletonClass3.shared
s.reset()
print(s === SingletonClass3.shared) // false

以上会输出 false,s 在重置之后,和 SingletonClass3.shared 不是同一个对象。因此,这样的重置数据方法不严谨。还是要老老实实把每个属性赋为默认值。

Objective-C 实现

严格单例模式

.h 文件

@interface SingletonClassOC : NSObject

+ (nonnull instancetype)shared;

+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
- (id)copy NS_UNAVAILABLE;
- (id)mutableCopy NS_UNAVAILABLE; @end

.m 文件

@implementation SingletonClassOC

+ (nonnull instancetype)shared {
static id _shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shared = [[self alloc] init];
});
return _shared;
} // Optional
- (void)reset {
// Reset all properties to default value
} @end

在 .h 文件中,用 NS_UNAVAILABLE 禁用初始化和拷贝方法,只允许用过 shared 方法访问唯一实例。

静态变量 _shared 持有唯一的实例,通过 shared 方法对外公开。由 dispatch_once 保证 _shared 只初始化一次。方法返回值的 nonnull 表示返回值不为空,这样写方便 Swift 调用。不加 nonnull,shared 方法在 Swift 中的返回值是 optional 类型 (SingletonClassOC?),不方便使用;加上 nonnull 则为 SingletonClassOC 类型。

如果需要,用 reset 方法重置数据。

不严格单例模式

.h 文件

@interface SingletonClassOC2 : NSObject

+ (nonnull instancetype)shared;

@end

.m 文件

@implementation SingletonClassOC2

+ (nonnull instancetype)shared {
static id _shared;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_shared = [[self alloc] init];
});
return _shared;
} - (id)copyWithZone:(NSZone *)zone {
SingletonClassOC2 *copiedObject = [[self.class allocWithZone:zone] init];
// Copy data to copiedObject
return copiedObject;
} - (id)mutableCopyWithZone:(NSZone *)zone {
SingletonClassOC2 *copiedObject = [[self.class allocWithZone:zone] init];
// Copy data to copiedObject
return copiedObject;
} - (void)reset {
// Reset all properties to default value
} @end

公开的 shared 方法与严格单例模式相同。外部可以通过 init 方法创建与 _shared 不同的实例。

重载 copyWithZone: 和 mutableCopyWithZone: 方法,在里面创建新实例,传递数据给新实例,返回新实例。外部可以通过 copy 或 mutableCopy 方法复制实例。

SDWebImage 中就用到不严格单例模式

NSObject 的类方法 new 相当于 alloc 和 init 方法。

转载请注明出处:http://www.cnblogs.com/silence-cnblogs/p/6776217.html

Swift、Objective-C 单例模式 (Singleton)的更多相关文章

  1. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  2. 【白话设计模式四】单例模式(Singleton)

    转自:https://my.oschina.net/xianggao/blog/616385 0 系列目录 白话设计模式 工厂模式 单例模式 [白话设计模式一]简单工厂模式(Simple Factor ...

  3. ooad单例模式-Singleton

                                                单例模式Singleton 主要作用是保证在Java应用程序中,一个类Class只有一个实例存在. 比如建立目录 ...

  4. iOS单例模式(Singleton)写法简析

    单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模式的要点有三个:一是某个类只能有一个实例: ...

  5. 浅谈设计模式--单例模式(Singleton Pattern)

    题外话:好久没写blog,做知识归纳整理了.本来设计模式就是个坑,各种文章也写烂了.不过,不是自己写的东西,缺少点知识的存在感.目前还没做到光看即能记住,得写.所以准备跳入设计模式这个大坑. 开篇先贡 ...

  6. 设计模式之——单例模式(Singleton)的常见应用场景

    单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...

  7. 设计模式之单例模式(Singleton Pattern)

    单例模式 单例模式(Singleton Pattern)在java中算是最常用的设计模式之一,主要用于控制控制类实例的数量,防止外部实例化或者修改.单例模式在某些场景下可以提高系统运行效率.实现中的主 ...

  8. 设计模式(4) -- 单例模式(Singleton)

    设计模式(4)  -- 单例模式(Singleton) 试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说 ...

  9. IOS单例模式(Singleton)

    IOS单例模式(Singleton)   单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 1.单例模式的要点: 显然单例模 ...

随机推荐

  1. Android中的WebView实战详解(一)

    一.为什么要用WebView? 1.兼容已有的项目2.可动态更新 二.WebView怎样使用? WebView是一个控件,如在布局中设置: <WebView android:id="@ ...

  2. 谈谈数据库中MyISAM与InnoDB区别 针对业务类型选择合适的表

    MyISAM:这个是默认类型,它是基于传统的ISAM类型, ISAM是Indexed Sequential Access Method (有索引的顺序访问方法) 的缩写,它是存储记录和文件的标准方法. ...

  3. windows phone 8.1开发:socket通信聊天

    本例用WPF程序做服务器端,windows phone程序做客户端.我们使用基于UDP协议的Socket通信.更多关于socket信息请查看:http://msdn.microsoft.com/zh- ...

  4. 关于连接数据库的T-SQL语句中的一种小技巧

    (编程生活中,我们经常会用到数据库.然后在通过T-SQL语句来对数据库进行操作的时候,遇到很多麻烦.话说昨天我就被困扰了一天.明明这个T-sql插数据的语句放在数据库运行的时候没有问题,到了java代 ...

  5. 一次SocketException:Connection reset 异常排查

    问题描述 上一期的需求上线之后,线上多了一个异常:Connection reset.如下: [2017-03-22 00:45:00 ERROR] [creativeAuditTaskSchedule ...

  6. 学习smart gwt 的一些好的网站

    最近在学smart gwt,这个框架和我们比较熟悉的SSH实现思路上有点不一样,因为技术是外国的,所以好多东西都是英文的,正因为是英文的,我们学到的东西才是最多最好的,好了,网站如下: gwt api ...

  7. Verilog读写文件

    在通过编写Verilog代码实现ram功能时,需要自己先计算寄存器的位数和深度再编写代码. 而如果需要在编写的ram中预置值的话,就需要使用Verilog语言编写程序读写文件,来将相应的数据赋给寄存器 ...

  8. 树莓派Raspberry中成功安装RobotFramework+Selenium

    [原创链接]:http://www.cnblogs.com/atsats/p/6666848.html 一般RobotFramework都是安装在Windows/Linux的PC机上,这里将简单介绍在 ...

  9. 老李分享:Web Services 架构 2

    服务传输层 这一层负责应用之间的消息传输.目前,该层包括了超文本传输协议(HTTP).简单邮件传输协议(SMTP).文件传输协议(FTP).以及一些新兴协议,比如块可扩展交换协议(BEEP). XML ...

  10. Unix文件 I/O(不带缓冲区的)上

    简介 Unix系统大多数文件i/o只需要:open.read.write.lseek.close这几个函数.但是某些时候我们也需要fcntl.ioctl.sync等函数配合使用.这些函数都是不带缓冲区 ...