单例模式怎么定义的,可能在不同的语言,不同的书中不完全一样,但是概况开来都应该是:一个类有且仅有一个实例,并且自行实例化向整个系统提供。

因此,首先你可能需要确定你是真的需要一个单例类,还是说仅仅是需要一个方便调用的实例化方法。如果你是真的需要一个单例类,那么你就应该确保这个单例类,有且仅有一个实例(不管怎么操作都只能获取到这个实例)。

最近看到一些github上的单例使用,别人的用法,有一些思考,然后写demo测试了下,就这个简单的单例也有一些坑呢,希望能给他人一些提醒。

Objective-C中的单例

我们通常在OC中实现一个单例方法都是这样:

1
2
3
4
5
6
7
8
9
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[[self class] alloc] init];
    });
    return instance;
}

可是这样就可以了么?我做了如下测试:

1
2
3
4
5
6
HLTestObject *objct1 = [HLTestObject sharedInstance];
NSLog(@"%@",objct1);
HLTestObject *objc2 = [[HLTestObject alloc] init];
NSLog(@"%@",objc2);
HLTestObject *objc3 = [HLTestObject new];
NSLog(@"%@",objc3);

看到这个测试,你想到打印结果了么?结果是这样的:

1
2
3
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf39515510>
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf395c4b70>
2016-05-23 12:52:57.095 PractiseProject[3579:81998] <hltestobject: 0x7fcf395c6890></hltestobject: 0x7fcf395c6890></hltestobject: 0x7fcf395c4b70></hltestobject: 0x7fcf39515510>

很明显,通过三种方式创建出来的是不同的实例对象,这就违背了单例类有且仅有一个实例的定义。

为了防止别人不小心利用alloc/init方式创建示例,也为了防止别人故意为之,我们要保证不管用什么方式创建都只能是同一个实例对象,这就得重写另一个方法,实现如下:

1
2
3
4
5
6
7
8
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}

再次用上面的测试代码,结果是这样的:

1
2
3
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0>
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0>
2016-05-23 12:57:37.396 PractiseProject[3618:83975] <hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0></hltestobject: 0x7f88b9488ac0>

好像用不同的构造方法,获取的都是同一个对象,你以为这样就完了?还早着呢!

一般我们的类里肯定都会有一些属性,然后我就添加了两个property:

1
2
3
@property (assign, nonatomic)   int  height;
@property (strong, nonatomic)   NSObject  *object;
@property (strong, nonatomic)   NSMutableArray  *arrayM;

而一些对象类的初始化,或者基础类型的默认值设置都是在init方法里,就像这样:

1
2
3
4
5
6
7
8
9
10
- (instancetype)init
{
    self = [super init];
    if (self) {
        _height = 10;
        _object = [[NSObject alloc] init];
        _arrayM = [[NSMutableArray alloc] init];
    }
    return self;
}

我重写了HLTestObject类的description方法:

1
2
3
4
5
6
7
8
9
- (NSString *)description
{
    NSString *result = @"";
    result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
    result = [result stringByAppendingFormat:@" height = %d,",self.height];
    result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
    result = [result stringByAppendingFormat:@" object = %p,",self.object];
    return result;
}

还是用上面的测试代码,测试结果是这样的:

1
2
3
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 20, arrayM = 0x7f8a5b422940, object = 0x7f8a5b4544e0,
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b4552e0, object = 0x7f8a5b45a710,
2016-05-23 13:14:43.684 PractiseProject[3781:92758] <hltestobject: 0x7f8a5b458450> height = 10, arrayM = 0x7f8a5b459770, object = 0x7f8a5b4544e0,</hltestobject: 0x7f8a5b458450></hltestobject: 0x7f8a5b458450></hltestobject: 0x7f8a5b458450>

可以看到,尽管使用的是同一个示例,可是他们的property值却不一样。

因为尽管没有为示例重新分配内存空间,但是因为又执行了init方法,会导致property被重新初始化。

所以我们需要修改单例的实现。

第一种:

可以将property的初始化或者默认值设置放到dispatch_once 的block内部:

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
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[[self class] alloc] init];
        instance.height = 10;
        instance.object = [[NSObject alloc] init];
        instance.arrayM = [[NSMutableArray alloc] init];
    });
    return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
- (NSString *)description
{
    NSString *result = @"";
    result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
    result = [result stringByAppendingFormat:@" height = %d,",self.height];
    result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
    result = [result stringByAppendingFormat:@" object = %p,",self.object];
    return result;
}

来看看测试结果:

1
2
3
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,
2016-05-23 13:29:14.856 PractiseProject[3909:99058] <hltestobject: 0x7fa72270c570> height = 20, arrayM = 0x7fa722716c10, object = 0x7fa7227140e0,</hltestobject: 0x7fa72270c570></hltestobject: 0x7fa72270c570></hltestobject: 0x7fa72270c570>

第二种:

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
static HLTestObject *instance = nil;
+ (instancetype)sharedInstance
{
    return [[self alloc] init];
}
- (instancetype)init
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super init];
        instance.height = 10;
        instance.object = [[NSObject alloc] init];
        instance.arrayM = [[NSMutableArray alloc] init];
    });
    return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [super allocWithZone:zone];
    });
    return instance;
}
- (NSString *)description
{
    NSString *result = @"";
    result = [result stringByAppendingFormat:@"<%@: %p>",[self class], self];
    result = [result stringByAppendingFormat:@" height = %d,",self.height];
    result = [result stringByAppendingFormat:@" arrayM = %p,",self.arrayM];
    result = [result stringByAppendingFormat:@" object = %p,",self.object];
    return result;
}

测试结果:

1
2
3
2016-05-23 13:31:44.824 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,
2016-05-23 13:31:44.825 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,
2016-05-23 13:31:44.825 PractiseProject[3939:100662] <hltestobject: 0x7fa9da711a70> height = 20, arrayM = 0x7fa9da707ca0, object = 0x7fa9da70a940,</hltestobject: 0x7fa9da711a70></hltestobject: 0x7fa9da711a70></hltestobject: 0x7fa9da711a70>

注意:

以上代码均是使用ARC的方式管理内存,如果你还在使用MRC(这也太不与时俱进了)。那你还需要重写 retain 和release方法,防止示例引用计数的改变。

Swift中的单例

利用Swift中的一些特性,Swift中的单例可以超级简单,like this:

1
2
3
class HLTestObject: NSObject {
    static let sharedInstance = HLTestObject();
}

可是这样就完了么?同样写一段测试代码:

1
2
3
4
let object1 = HLTestObject.sharedInstance;
print(object1);
let object2 = HLTestObject();
print(object2);

打印结果却是这样的:

1
2
<swiftproject.hltestobject: 0x7f90ebc74e50>
<swiftproject.hltestobject: 0x7f90ebe5cf40></swiftproject.hltestobject: 0x7f90ebe5cf40></swiftproject.hltestobject: 0x7f90ebc74e50>

所以,我们必须禁用到构造方法:

1
2
3
4
5
class HLTestObject: NSObject {
    static let sharedInstance = HLTestObject();
    private override init() {
    }
}

如果有实例属性需要初始化,就可以这样:

1
2
3
4
5
6
7
8
9
10
11
class HLTestObject: NSObject {
    var height = 10;
    var arrayM: NSMutableArray
    var object: NSObject
    static let sharedInstance = HLTestObject();
    private override init() {
        object = NSObject()
        arrayM = NSMutableArray()
        super.init()
    }
}

当然,由于Swift的特性,在Swift中创建单例的方式也不止一种,需要注意的是要确保该类有且仅有一个实例就OK了。

iOS中单例需要注意的的更多相关文章

  1. IOS中单例NSUserDefaults的使用(转)

    一.了解NSUserDefaults以及它可以直接存储的类型 http://my.oschina.net/u/1245365/blog/294449 NSUserDefaults是一个单例,在整个程序 ...

  2. iOS中单例创建时不严格造成的问题和解决方法

    这次项目中遇到了一个单例创建不严格造成了的问题.简单说来就是在有的地方使用了alloc创建了多个实例,当然如果严格按照接口的方法调用是不会有问题的,但是如果项目碰到有不太熟悉的人使用时在处理时就会出现 ...

  3. 【iOS 单例设计模式】底层解析与运用

    [iOS 单例设计模式]底层解析与运用 一.单例设计名词解释: (官方解释)单例模式确保一个类只有一个实例,自行提供这个实例并向整个系统提供这个实例.(形象比喻)程序 — 公司   单例实例 - 管理 ...

  4. swift 中单例的写法

    在swift中单例的写法和oc的有所不同,在书写的时候又分很多种写法,,如果一个.swift 文件只创建了一个类,可以用那种dispatch_once的写法,如果一个.swift文件中有很多类的存在, ...

  5. ios 单例的再次理解

    单例模式    在建模的时候,如果这个东西确实只需要一个对象,多余的对象都是无意义的,那么就考虑用单例模式.比如定位管理(CLLocationManager),硬件设备就只有一  个,弄再多的逻辑对象 ...

  6. iOS单例创建的一点疑惑

    线程安全的单例常用写法, +(AccountManager *)sharedManager{ static AccountManager *defaultManager = nil; disptch_ ...

  7. beanfactory中单例bean的初始化过程(一)

    Date 10.06 pm Point 完成beanfactory中单例bean的初始化 beanFactory.preInstantiateSingletons() 拿到所有的bean定义信息(在 ...

  8. iOS 单例传值遇见问题

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

  9. iOS单例的两种实现

    单例模式算是开发中比较常见的一种模式了.在iOS中,单例有两种实现方式(至少我目前只发现两种).根据线程安全的实现来区分,一种是使用@synchronized,另一种是使用GCD的dispatch_o ...

随机推荐

  1. Color.js增强你对颜色的控制

    往逝之因 不要低头,皇冠会掉...  可你又没有皇冠 Color.js 增强你对颜色的控制 阅读目录 轻松管理颜色--color.js库 使用color.js Accessor Methods 你该知 ...

  2. 自制VTP实验总结

    (packet tracer模拟器) 6.1)实验拓扑 //绿色:通:橙色:不通 //sw0是根桥:全通 Pc0:ip 192.168.1.1 Pc1: ip 192.168.1.2 Pc2:ip 1 ...

  3. CentOS7中将home迁移到/下的命令 CentOS7中将home迁移到/下的命令

    # mkdir -p /backup # cp -r /home/* /backup # umount /home #  df -hl # fdisk -l # lvremove /dev/cento ...

  4. 第二次项目冲刺(Beta阶段)5.23

    1.提供当天站立式会议照片一张 会议内容: ①检查前一天的任务情况,将遇到的瓶颈反馈,看看团队成员是否有好的建议. ②制定新一轮的任务计划. 2.每个人的工作 (1)工作安排 队员 今日进展 明日安排 ...

  5. 个人作业2————英语学习APP的案例分析

    必应词典案例分析 第一部分 调研, 评测 1.下载并使用 第一次使用必应词典,安装完打开便是这样的界面,第一印象还行,界面平平无奇,比较简洁,上面分四个模块,这样一眼看去感觉功能比较单一 使用了下例句 ...

  6. 团队作业8——第二次项目冲刺(Beta阶段)Day2--5.19

    1.展开站立式会议: 会议内容:①汇报一天大家任务的完成成果. ②新成员对学到的知识进行交流,并向老成员提问. ③根据大家的进度制定新一轮的任务计划. 2.每个人的工作分配 队员 今日任务 明日任务 ...

  7. Python笔记2.1

    所有类型如下图: 一 基础数据类型 1)数字类型 复制代码 >>> 2/2+2*2 5.0 >>> (50-5*6)/4 5.0 >>> 8/5 ...

  8. evak购物车--团队博客

    1. 团队名称.团队成员介绍(需要有照片) 团队名称:evak 团队成员介绍:陈凯欣,计算机工程学院网络工程1512,学号为201521123034:邱晓娴,计算机工程学院网络工程1512,学号为20 ...

  9. 201521123025《java程序设计》第12周学习总结

    #1. 本周学习总结 #2. 书面作业 将Student对象(属性:int id, String name,int age,double grade)写入文件student.data.从文件读出显示. ...

  10. 201521123003《Java程序设计》第10周学习笔记

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结异常与多线程相关内容. 2. 书面作业 本次PTA作业题集异常.多线程 1.finally 题目4-2 1.1 截图你的提交结果(出 ...