iOS中的单例你用对了?

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

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

最近看到一些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了。

时间: 2024-08-08 11:04:21

iOS中的单例你用对了?的相关文章

iOS中创建单例的两种方式

刚刚开始做iOS开发的时候,不知道怎么创建单例,在网上搜的也大多数都不太全,所以总结了一下创建单例的两种方式 首先在.h文件中声明类方法 1 +(instancetype)sharedUtils; 然后在.m文件中实现它,实现的方法有两种 第一种,在创建一个对象时先判断在程序中有没有创建过这个对象 1 static PublicUtils *DefaultManager = nil; 2 3 +(instancetype)sharedUtils 4 { 5 if (!DefaultManager

IOS中实现单例

在IOS中,所有对象的内存分配的方法都会调用allocWithZone,比如构造函数alloc,所以重写这个方法就可以实现单例. Xcode中预先写好了实现代码的快捷指令,敲dispatch_once就会看到.这个是有GCD实现的单例代码. 实现代码如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 +(id)allocWithZone:(struct _NSZone *)zone {     static LYDemo * instance;     //

IOS中的单例设计模式

单例设计模式是IOS开发中一种很重要很常用的一种设计模式.它的设计原理是无论请求多少次,始终返回一个实例,也就是一个类只有一个实例.下面是苹果官方文档中关于单例模式的图片: 如图所示,左边的图是默认的多例模式,只要发送请求去创建对象,就会得到一个新的对象:而右侧的图是单例模式,发送多个请求创建对象,但是最后返回的始终是同一个. 因为创建单例类的实例是在确保没有其他实例的情况下才创建的,并且在程序中使用的过程中始终是同一个实例,因此单例类可以作为提供一个全局访问资源的类,比如NSUserDefau

iOS 中的单例设计模式

单例设计模式:在它的核心结构中只包含一个被称为单例类的特殊类.例如文件管理中的NSUserDefault,应用程序中的UIApplication,整个应用程序就这一个单例类,负责应用程序的一些操作,单例在那个文件下都能取得到. 通过单例设计模式可以保证系统中一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节省系统资源.如果希望在系统中某个类的对象只能存在一个,单例模式是最好的选择. 下面来点实在的,创建单例代码上 方法1:基于线程安全创建一个单例 .h做一下声明 + (id)

iOS中的单例

#import "Singleton.h" @implementation Singleton static Singleton *singleton = nil; + (Singleton *)shareSingleton { @synchronized(self) { if (!singleton) { singleton = [[self alloc]init]; } return singleton; } } + (id)alloc { @synchronized(self)

iOS开发——多线程OC篇&amp;多线程中的单例

多线程中的单例 1 #import "DemoObj.h" 2 3 @implementation DemoObj 4 5 static DemoObj *instance; 6 7 8 9 // 在iOS中,所有对象的内存空间的分配,最终都会调用allocWithZone方法 10 // 如果要做单例,需要重写此方法 11 // GCD提供了一个方法,专门用来创建单例的 12 + (id)allocWithZone:(struct _NSZone *)zone 13 { 14 sta

Swift中编写单例的正确方式

Swift中编写单例的正确方式 2015-12-07 10:23 编辑: yunpeng.hu 分类:Swift 来源:CocoaChina翻译活动 14 10647 Objective-CSwift单例 招聘信息: Cocos2d-x 工程师 cocos2dx手游客户端主程 wp开发 iOS开发工程师 iOS软件工程师 iOS研发工程师 iOS讲师 iOS开发工程师 iOS高级开发工程师 iOS 高级软件工程师 iOS高级开发工程师 本文由CocoaChina译者leon(社区ID)翻译自kr

IOS开发之单例设计模式

本文将从四个方面对IOS开发中的单例设计模式进行讲解: 一.什么是单例设计模式 二.我们为什么要用单例设计模式 三.单例设计模式的基本用法 四.自定义单例设计模式代码的封装 一.什么是单例设计模式 所谓单例,即是单个的实例化对象,保证一个类有且仅有一个实例.通常情况下,当我们对一个类实例化时(如:alloc.new等),并不能保证每次实例化的对象是唯一的实例.那么为了保证该类可在多次实例化的过程中保证内存地址不变,就需要引入单例设计模式. 二.我们为什么要用单例设计模式 1.Singleton

IOS开发模式——单例

单例的模式在网上有很多,今天发下我个人对单例模式的理解.整个app中只存在一个实例,也只会进行一次实例,在实例完成之后是不可以人释放的(当App关闭之后,等系统自己回收). 也就是说,如果我们写得某个类符合了上述条件,那么我们也可以称这个类为单例. 在非ARC的工程中,我们需要针对alloc,retain,copy等会增加retaincount的参数加以控制,对release和autorelease等减少retailcount的操作增加控制,以确保单一实例,绝不释放. 在ARC的工厂中,由于,内