单例的生命周期
iOS app 中单例一经创建,全局共享单例对象,单例的生命周期跟随app生命周期,那它的原理是什么呢?
答:先看单例写法:
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
| //全局的变量,指的就是Singleton这个对象,静态全局变量,始终指向实例化出的对象 static id instance; + (instancetype)shareInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ //创建类 instance = [[self alloc]init]; }); return instance; } //创建alloc init 时候创建,会调用allocWithZone ,保证创建出来这个对象是唯一的 + (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ //返回自己 instance = [super allocWithZone:zone]; }); return instance; } //防止外界拷贝造成多个实例 - (id)copyWithZone:(NSZone *)zone{ return instance; }
|
首先使用 static 修饰符,声明了单例对象,这就意味着这个单例对象是在程序的数据区进行存储的,而数据区会跟随 app打开申请 app关闭释放。实现了单例生命周期跟随app的生命周期,这也解释了为什么每次打开app(非后台状态)单例对象就会重新初始化,造成单例上次保存的数据丢失。
那单例对象生命周期是否应该由 MRC/ARC 控制呢?
答案也是否定的,因为无论 ARC 还是 MRC 都是管理用来管理堆上对象的生命周期的,而数据区的单例释放与否是由系统控制的,因此不应受他们控制,需要注意的是,在使用 MRC 时,单例调用 retain 确实会引起计数增加,而调用 release 则会直接崩溃,为了避免单例收到引用计数的干扰造成意想不到的后果,在 MRC 下需要覆盖retain autorelease retainCount release 这几个方法。重写这几个方法是为了单例能在MRC中使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #pragma mark - MRC中需要覆盖的方法, ARC与MRC的整合 #if !__has_feature(objc_arc) - (id)retain { return self; } - (id)autorelease { return self; } - (oneway void)release { } - (NSUInteger)retainCount { return UINT_MAX; } #endif
|