首页 > 代码库 > iOS多线程技术—单例模式(ARC)与(MRC)

iOS多线程技术—单例模式(ARC)与(MRC)

iOS多线程技术—单例模式(ARC)

一、简单说明:

设计模式:多年软件开发,总结出来的一套经验、方法和工具

java中有23种设计模式,在ios中最常用的是单例模式和代理模式。

 

二、单例模式说明

(1)单例模式的作用 :可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问,从而方便地控制了实例个数,并节约系统资源。

(2)单例模式的使用场合:在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次),应该让这个类创建出来的对象永远只有一个。

(3)单例模式在ARC\MRC环境下的写法有所不同,需要编写2套不同的代码

可以用宏判断是否为ARC环境

#if __has_feature(objc_arc) // ARC#else// MRC#endif

 

(4)在ARC中,单例模式的实现思路

在.m中保留一个全局的static的实例   static id _instance; 

1)重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全)

+ (id)allocWithZone:(struct _NSZone *)zone{    @synchronized(self) {        if (!_instance) {            _instance = [super allocWithZone:zone];        }    }    return _instance;}

 

2)提供1个类方法让外界访问唯一的实例

+ (instancetype)sharedSoundTool{    @synchronized(self) {        if (!_instance) {            _instance = [[self alloc] init];        }    }    return _instance;}

 

3)实现copyWithZone:方法

+ (id)copyWithZone:(struct _NSZone *)zone {     return _instance; }

 

 

 (5)非ARC中(MRC),单例模式的实现(比ARC多了几个步骤)

实现内存管理方法

- (id)retain { return self; }

- (NSUInteger)retainCount { return 1; }

- (oneway void)release {}

- (id)autorelease { return self; }

  

三、单例模式(ARC)

1.说明

重写allocWithzone:方法,控制内存分配。因为alloc内部会调用该方法,每次调用allocWithzone:方法,系统都会创建一块新的内存空间。

alloc方法中:永远只分配一次内存

init方法中:保证所有的MP3数据都只加载一次。

 

 2.代码示例

创建一个音频工具类,继承子NSObject类。

在该类中实现以下代码,观察:

 1 // 2 //  YYAudioTool.m 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYAudioTool.h"10 @interface YYAudioTool ()11 //用来保存mp3文件12 @property(nonatomic,strong)NSMutableDictionary *muscis;13 @end14 @implementation YYAudioTool15 //构造方法16 -(id)init17 {18     if (self=[super init]) {19         //加载所需的音乐资源20         //....21 //        self.muscis=[NSMutableDictionary dictionary];22 //        self.muscis[@"1.mp3"]=1mp3数据;23 //        self.muscis[@"2.mp3"]=2mp3数据;24     }25     return self;26 }27 28 //两个方法的调用29 +(id)alloc30 {31     NSLog(@"alloc----");32     return [super alloc];33 }34 35 //控制内存分配,每次调用allocWithzone:方法,系统都会创建一块新的内存空间36 +(id)allocWithZone:(struct _NSZone *)zone37 {38     NSLog(@"allocWithZone---");39     return [super allocWithZone:zone];40 }41 42 43 44 @end
View Code

 

在主控制器中,创建工具类对象:

 

打印结果:

说明:在alloc内部会调用更底层的方法allocWithZone方法分配内存空间,上面的代码创建了四个不同的对象。 

3.单例模式:设计思路 

(1)永远只分配一块内存来创建对象

(2)提供一个类方法,返回内部唯一的一个变量

(3)最好保证init方法也只初始化一次

代码示例:

创建一个音频工具类,继承子NSObject类。

在该类中按照设计思路实现以下代码:

YYAudioTool.m文件

 1 // 2 //  YYAudioTool.m 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYAudioTool.h"10 @interface YYAudioTool ()11 //用来保存mp3文件12 @property(nonatomic,strong)NSMutableDictionary *muscis;13 @end14 15 @implementation YYAudioTool16 //定义一份变量(整个程序运行过程中,只有一份)17 static id _instace;18 //单例模式:设计19 //(1)永远只分配一块内存来创建对象20 //(2)提供一个类方法,返回内部唯一的一个变量21 //(3)最好保证init方法也只初始化一次22 23 //构造方法24 -(id)init25 {26 //    __block id obj=nil;27     static id obj=nil;28     static dispatch_once_t onceToken;29     dispatch_once(&onceToken, ^{30         if ((obj=[super init]) != nil) {31             //加载所需的音乐资源32             //....33             //        self.muscis=[NSMutableDictionary dictionary];34             //        self.muscis[@"1.mp3"]=1mp3数据;35             //        self.muscis[@"2.mp3"]=2mp3数据;36         }37     });38     self=obj;39 40     return self;41 }42 43 44 //重写该方法,控制内存的分配,永远只分配一次存储空间45 +(id)allocWithZone:(struct _NSZone *)zone46 {47 48     //里面的代码只会执行一次49     static dispatch_once_t onceToken;50     dispatch_once(&onceToken, ^{51         _instace=[super allocWithZone:zone];52     });53     return _instace;54 }55 56 //类方法57 +(id)sharedAudioTool58 {59   //里面的代码永远都只执行一次60     static dispatch_once_t onceToken;61     dispatch_once(&onceToken, ^{62         _instace=[[self alloc]init];63     });64     return _instace;65 }66 67 +(id)copyWithZone:(struct _NSZone *)zone68 {69     return _instace;70 }71 @end
View Code

YYAudioTool.h文件

1 #import <Foundation/Foundation.h>2 3 @interface YYAudioTool : NSObject4 //提供一个类方法,返回内部唯一的一个变量5 +(id)sharedAudioTool;6 @end

主控制器中创建对象:

 1 // 2 //  YYViewController.m 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYViewController.h"10 #import "YYAudioTool.h"11 12 @interface YYViewController ()13 14 @end15 16 @implementation YYViewController17 18 - (void)viewDidLoad19 {20     [super viewDidLoad];21 //    YYAudioTool *tool1=[[YYAudioTool alloc]init];22 //    YYAudioTool *tool2=[[YYAudioTool alloc]init];23     YYAudioTool *tool1=[YYAudioTool sharedAudioTool];24     YYAudioTool *tool2=[YYAudioTool sharedAudioTool];25     YYAudioTool *tool3=[[YYAudioTool alloc]init];26     YYAudioTool *tool4=[[YYAudioTool alloc]init];27     NSLog(@"%p--%p--%p--%p",tool1,tool2,tool3,tool4);28 }29 30 @end
View Code

 

观察打印结果:

 

说明:整个程序中只创建一个对象实例。

4.static补充:

注意:static id instace=nil;和static id instace;instace=nil;的区别

 

 

 

 四、非ARC模式下的单例模式

1.说明:把一个项目修改为非ARC的

2.MAC下单例模式代码示例:

新建一个工具类,让该类继承自NSObject。

YYAudioTool.m文件

 1 // 2 //  YYAudioTool.m 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYAudioTool.h"10 @interface YYAudioTool ()11 //用来保存mp3文件12 @property(nonatomic,strong)NSMutableDictionary *muscis;13 @end14 15 @implementation YYAudioTool16 //定义一份变量(整个程序运行过程中,只有一份)17 static id _instace;18 //单例模式:设计19 //(1)永远只分配一块内存来创建对象20 //(2)提供一个类方法,返回内部唯一的一个变量21 //(3)最好保证init方法也只初始化一次22 23 //构造方法24 -(id)init25 {26 //    __block id obj=nil;27     static id obj=nil;28     static dispatch_once_t onceToken;29     dispatch_once(&onceToken, ^{30         if ((obj=[super init]) != nil) {31     32         }33     });34     self=obj;35 36     return self;37 }38 39 40 //重写该方法,控制内存的分配,永远只分配一次存储空间41 +(id)allocWithZone:(struct _NSZone *)zone42 {43 44     //里面的代码只会执行一次45     static dispatch_once_t onceToken;46     dispatch_once(&onceToken, ^{47         _instace=[super allocWithZone:zone];48     });49     return _instace;50 }51 52 //类方法53 +(id)sharedAudioTool54 {55   //里面的代码永远都只执行一次56     static dispatch_once_t onceToken;57     dispatch_once(&onceToken, ^{58         _instace=[[self alloc]init];59     });60     return _instace;61 }62 63 //重写release方法64 //oneway :分布式对象65 -(oneway void)release66 {67 }68 69 //不管调用哪个方法,返回的都是唯一的实例,所以这里self和instace是一样的70 -(id)autorelease71 {72     return self;73 }74 75 -(id)retain76 {77     return self;78 }79 80 -(NSUInteger)retainCount81 {82     return 1;83 }84 85 +(id)copyWithZone:(struct _NSZone *)zone86 {87     return _instace;88 }89 @end
View Code

 

YYAudioTool.h文件

 1 // 2 //  YYAudioTool.h 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 11 @interface YYAudioTool : NSObject12 //提供一个类方法,返回内部唯一的一个变量13 +(id)sharedAudioTool;14 @end
View Code

 

主控制器中创建对象:

 1 // 2 //  YYViewController.m 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import "YYViewController.h"10 #import "YYAudioTool.h"11 12 @interface YYViewController ()13 14 @end15 16 @implementation YYViewController17 18 - (void)viewDidLoad19 {20     [super viewDidLoad];21     YYAudioTool *tool1=[YYAudioTool sharedAudioTool];22     YYAudioTool *tool2=[YYAudioTool sharedAudioTool];23     YYAudioTool *tool3=[YYAudioTool sharedAudioTool];24     YYAudioTool *tool4=[[YYAudioTool alloc]init];25     26     NSLog(@"%p--%p--%p--%p",tool1,tool2,tool3,tool4);27     28     //对象创建只会,release对象会销毁,无法再创建新的对象(因为单例),所以还需要重写release方法29     [tool1 release];30     [tool2 release];31     [tool3 release];32     [tool4 release];33 }34 35 @end
View Code

 

打印结果:

说明:整个程序过程中,只创建一个对象实例。

 

 五、把单例代码定义为一个带参数的宏

1.新的困扰

弊端:如果又创建一个新的类,是否又要把文件代码拷贝一份,所以这里可以考虑把固定的代码写成宏。

由于项目中代码经常有移植的需要,要求项目中又有ARC的,又有非ARC的,应该怎么应用单例模式?

不管项目是ARC的还是非ARC的,这个宏都有用。可以先判断编译器的环境,判断当前环境是否是ARC的。

条件编译的使用:

2.使用条件编译,并把单例模式的代码定义为宏。

新建一个.h头文件

  把代码定义为宏,头文件中的代码如下:

 1 // ## : 连接字符串和参数 2 #define singleton_h(name) + (instancetype)shared##name; 3  4 #if __has_feature(obj_arc)  //如果是ARC 5 #define singleton_m(name)  6 static id _instance;  7 + (id)allocWithZone:(struct _NSZone *)zone  8 {  9     static dispatch_once_t onceToken; 10     dispatch_once(&onceToken, ^{ 11         _instance = [super allocWithZone:zone]; 12     }); 13     return _instance; 14 } 15 16 + (instancetype)shared##name 17 { 18     static dispatch_once_t onceToken; 19     dispatch_once(&onceToken, ^{ 20         _instance = [[self alloc] init]; 21     }); 22     return _instance; 23 } 24 25 + (id)copyWithZone:(struct _NSZone *)zone 26 { 27     return _instance; 28 }29 30 #else //非ARC31 #define singleton_m(name) 32 static id _instance; 33 + (id)allocWithZone:(struct _NSZone *)zone 34 { 35     static dispatch_once_t onceToken; 36     dispatch_once(&onceToken, ^{ 37         _instance = [super allocWithZone:zone]; 38     }); 39     return _instance; 40 } 41  42 + (instancetype)shared##name 43 { 44     static dispatch_once_t onceToken; 45     dispatch_once(&onceToken, ^{ 46         _instance = [[self alloc] init]; 47     }); 48     return _instance; 49 } 50  51 - (oneway void)release 52 { 53  54 } 55  56 - (id)autorelease 57 { 58     return _instance; 59 } 60  61 - (id)retain 62 { 63     return _instance; 64 } 65  66 - (NSUInteger)retainCount 67 { 68     return 1; 69 } 70  71 + (id)copyWithZone:(struct _NSZone *)zone 72 { 73     return _instance; 74 }75 #endif
View Code

 

在程序中的应用:

控制器问价代码如下:

 1 #import "YYViewController.h" 2 #import "YYAudioTool.h" 3  4 @interface YYViewController () 5  6 @end 7  8 @implementation YYViewController 9 10 - (void)viewDidLoad11 {12     [super viewDidLoad];13     YYAudioTool *tool1=[YYAudioTool sharedAudioTool];14     15     //对象创建后,需要考虑16 //    (1)alloc init17 //    (2)release18 //    (3)copy copy内部会调用另外一个方法copywithzone19 //    (4)autorelease20 //    21     YYAudioTool *tool2=[YYAudioTool sharedAudioTool];22     YYAudioTool *tool3=[YYAudioTool sharedAudioTool];23     NSLog(@"%p---%p---%p",tool1,tool2,tool3);24     [tool2 release];25     [tool3 release];26     [tool1 release];27 28 }29 30 @end
View Code

 

工具类的头文件代码如下:

 1 #import "YYViewController.h" 2 #import "YYAudioTool.h" 3  4 @interface YYViewController () 5  6 @end 7  8 @implementation YYViewController 9 10 - (void)viewDidLoad11 {12     [super viewDidLoad];13     YYAudioTool *tool1=[YYAudioTool sharedAudioTool];14     15     //对象创建后,需要考虑16 //    (1)alloc init17 //    (2)release18 //    (3)copy copy内部会调用另外一个方法copywithzone19 //    (4)autorelease20 //    21     YYAudioTool *tool2=[YYAudioTool sharedAudioTool];22     YYAudioTool *tool3=[YYAudioTool sharedAudioTool];23     NSLog(@"%p---%p---%p",tool1,tool2,tool3);24     [tool2 release];25     [tool3 release];26     [tool1 release];27 28 }29 30 @end
View Code

 

工具类的实现部分代码如下:

 1 // 2 //  YYAudioTool.h 3 //  06-单例模式1 4 // 5 //  Created by apple on 14-6-25. 6 //  Copyright (c) 2014年 itcase. All rights reserved. 7 // 8  9 #import <Foundation/Foundation.h>10 #import "Singleton.h"11 12 @interface YYAudioTool : NSObject13 ////提供一个类方法,返回内部唯一的一个变量14 //+(id)sharedAudioTool;15 singleton_h(AudioTool)16 @end
View Code

 

补充说明:如果把代码下载dispatch_once里面,那么它内部默认会进行加锁。

 

六、补充

问题:ARC和非ARC单例模式的区别?

由于非ARC是进行手动内存管理,所以需要注意下面一个方法,在项目中通常使用宏。

- (id)retain { return self; }

- (NSUInteger)retainCount { return 1; }

- (oneway void)release {}

- (id)autorelease { return self; }

iOS多线程技术—单例模式(ARC)与(MRC)