首先咱们要明白下面三个问题:设计模式
单例模式(Singleton):单例模式确保对于一个给定的类只有一个实例存在,这个实例有一个全局惟一的访问点。安全
例如:[NSUserDefaults standardUserDefaults],[UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager]等,全部的这些方法都返回一个单例对象,苹果公司大量使用了此模式。网络
通常状况下,项目中的工具类使用单例模式比较合适,工具类通常是整个项目都用的上,可是只用一个,因此不必建立多个。工具
单例的实现应该分为两种状况:非ARC(MRC)和ARC学习
MRC状况下咱们的单例模式实现以下:spa
1> 在不使用单例模式的状况下打印三个实例对象,他们指向的地址是不同的。示例代码以下:线程
DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];设计
DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];code
DHNetworkTool *tool3 = [[DHNetworkTool alloc]init];对象
NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);
2> 咱们但愿经过DHNetworkTool建立的对象是同一个,也就是只分配一块内存空间,那咱们应该去重写alloc方法,由于alloc方法负责分配内存空间的。
+ (instancetype)alloc {} + (instancetype)allocWithZone:(struct _NSZone *)zone {}
如今发现有以上两个方法,那么咱们应该重写哪一个alloc方法呢?个人建议是重写后者也就是+ (instancetype)allocWithZone:(struct _NSZone *)zone {}
为何?
由于alloc内部会调用allocWithZone,也就是说allocWithZone方法更底层。也就是说咱们实现alloc方法的话就只能拦截alloc方法,可是实现allocWithZone方法的话,任何内存分配的方法咱们都能拦截。
Zone的意思就是空间,当你调用这个方法的时候,系统会自动给你传递一块内存空间,Zone就是系统分配给开发者APP的内存空间。
注意:在咱们实现allocWithZone能调用父类的方法吗?不能!若是调用了就至关于咱们没写!!!咱们的目的就是不要使用父类的,咱们本身作本身的事情。
3> 咱们须要建立一个全局变量为了避免让别人访问,咱们应该使用static修饰一下
1 //用static是为了避免让别人访问这个变量 2 static DHNetworkTool *_networkTool = nil; 3 4 //alloc内部会调用allocWithZone,咱们实现alloc方法的话就只能拦截alloc方法,可是实现allocWithZone方法的话,任何内存分配的方法咱们都能拦截 5 + (instancetype)allocWithZone:(struct _NSZone *)zone { 6 7 //若是在这里调用父类的方法至关于没有重写allocWithZone方法 8 // return [super allocWithZone:zone]; 9 10 //这样判断的话就能保证咱们返回的都是_networkTool这个对象了 11 if (_networkTool == nil) { 12 13 static dispatch_once_t onceToken; 14 dispatch_once(&onceToken, ^{//保证线程安全,并且这个代码只会被执行一次 15 16 //在这里能够调用父类的方法了 17 _networkTool = [super allocWithZone:zone]; 18 }); 19 20 21 } 22 return _networkTool; 23 }
4> 咱们初步实现后能够再次验证是否建立的对象是同一个
打印结果能够验证是同一个对象,由于实例化对象的时候要调用allocWithZone方法,在该方法实例化对象的代码只会走一次,因此保证每次实例化的对象都是同一个。
5> 在MRC环境下咱们这样写是不严谨的,由于还可能会调用release方法!若是调用了release方法那就意味着这个对象完了,下次在调用alloc方法的时候就没办法建立对象了,由于在allocWithZone方法中实例化对象的代码只走一次。
为了不这种状况,咱们还须要重写release方法,拦截对象被释放。重写release方法就是为了保证整个程序都有这个单例对象。
//重写release防止对象被释放,由于对象一旦被释放就不再能生成了。 - (oneway void)release { }
6> 除了release方法外,咱们还须要重写retain方法。为何呢?由于咱们是单例模式,这个对象只会被建立一次,那么咱们就一直让他的引用计数为1,不要增长,不要让其增长。那么咱们还须要再重写retainCount方法,返回1就行了。
1 //使单例对象引用计数不增长 2 -(instancetype)retain { 3 4 return self; 5 } 6 7 //使单例对象引用计数一致为1 8 - (NSUInteger)retainCount { 9 10 return 1; 11 }
7> allocWithZone release retainCount三者不可缺一。
8> 单例模式已经基本实现了,最后一步就是咱们应该仿照系统实现一个类方法返回咱们的单例对象。
注意:若是直接返回对象的话,那么这个对象就会一直为空,因此须要在类方法里调用alloc ini方法。可是每次都调用init方法的话,咱们的对象每次都要被初始化,因此要重写init方法,保证这个单例对象只执行一次初始化。为何不判断对象为空呢?由于调用init的时候要先执行alloc方法。
1 + (instancetype)sharedNetworkTool { 2 3 //直接返回这个对象意味着它一直为空 4 // return _networkTool; 5 return [[self alloc] init]; 6 } 7 8 //每次都调用init方法的话,咱们的对象每次都要被初始化,因此要保证init只执行一次 9 -(instancetype)init { 10 11 static dispatch_once_t onceToken; 12 dispatch_once(&onceToken, ^{ 13 _networkTool = [super init]; 14 }); 15 return _networkTool; 16 }
最后再次验证一下,打印结果以下:
DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];
DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];
DHNetworkTool *tool3 = [DHNetworkTool sharedNetworkTool];
NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);
ARC下单例模式的实现相对比较简单,下面就只是展现.m文件部分源码,不作赘述了。
1 static id _instance = nil; 2 3 + (id)allocWithZone:(struct _NSZone *)zone 4 { 5 if (_instance == nil) { 6 static dispatch_once_t onceToken; 7 dispatch_once(&onceToken, ^{ // 安全(这个代码只会被调用一次) 8 _instance = [super allocWithZone:zone]; 9 }); 10 } 11 return _instance; 12 } 13 14 - (id)init 15 { 16 static dispatch_once_t onceToken; 17 dispatch_once(&onceToken, ^{ 18 _instance = [super init]; 19 }); 20 return _instance; 21 } 22 23 + (instancetype)sharedDataTool 24 { 25 return [[self alloc] init]; 26 }
最后一点,在公司项目开发过程当中咱们一般会把单例模式的实现抽取成一个宏,放到.PCH文件中,这样方便项目组中的每一个人去使用,再次也作一下抽取和代码的展现吧,能够直接拿到工程中使用。
1 //实现单例设计模式 2 3 // .h文件的实现 4 #define SingletonH(methodName) + (instancetype)shared##methodName; 5 6 // .m文件的实现 7 #if __has_feature(objc_arc) // 是ARC 8 #define SingletonM(methodName) \ 9 static id _instace = nil; \ 10 + (id)allocWithZone:(struct _NSZone *)zone \ 11 { \ 12 if (_instace == nil) { \ 13 static dispatch_once_t onceToken; \ 14 dispatch_once(&onceToken, ^{ \ 15 _instace = [super allocWithZone:zone]; \ 16 }); \ 17 } \ 18 return _instace; \ 19 } \ 20 \ 21 - (id)init \ 22 { \ 23 static dispatch_once_t onceToken; \ 24 dispatch_once(&onceToken, ^{ \ 25 _instace = [super init]; \ 26 }); \ 27 return _instace; \ 28 } \ 29 \ 30 + (instancetype)shared##methodName \ 31 { \ 32 return [[self alloc] init]; \ 33 } \ 34 + (id)copyWithZone:(struct _NSZone *)zone \ 35 { \ 36 return _instace; \ 37 } \ 38 \ 39 + (id)mutableCopyWithZone:(struct _NSZone *)zone \ 40 { \ 41 return _instace; \ 42 } 43 44 #else // 不是ARC 45 46 #define SingletonM(methodName) \ 47 static id _instace = nil; \ 48 + (id)allocWithZone:(struct _NSZone *)zone \ 49 { \ 50 if (_instace == nil) { \ 51 static dispatch_once_t onceToken; \ 52 dispatch_once(&onceToken, ^{ \ 53 _instace = [super allocWithZone:zone]; \ 54 }); \ 55 } \ 56 return _instace; \ 57 } \ 58 \ 59 - (id)init \ 60 { \ 61 static dispatch_once_t onceToken; \ 62 dispatch_once(&onceToken, ^{ \ 63 _instace = [super init]; \ 64 }); \ 65 return _instace; \ 66 } \ 67 \ 68 + (instancetype)shared##methodName \ 69 { \ 70 return [[self alloc] init]; \ 71 } \ 72 \ 73 - (oneway void)release \ 74 { \ 75 \ 76 } \ 77 \ 78 - (id)retain \ 79 { \ 80 return self; \ 81 } \ 82 \ 83 - (NSUInteger)retainCount \ 84 { \ 85 return 1; \ 86 } \ 87 + (id)copyWithZone:(struct _NSZone *)zone \ 88 { \ 89 return _instace; \ 90 } \ 91 \ 92 + (id)mutableCopyWithZone:(struct _NSZone *)zone \ 93 { \ 94 return _instace; \ 95 } 96 97 #endif
写了一个小小的Demo作简单的展现,欢迎你们一块儿学习交流哇。连接: http://pan.baidu.com/s/1bpHjYUF 密码: ti7y