iOS开发笔记之八十——单例的总结笔记

    技术2022-07-11  96

    ******阅读完此文,大概需要10分钟******

    一、单例的创建

    #import "MDInstanceManager.h" @implementation MDInstanceManager static MDInstanceManager *shareInstance = nil; static dispatch_once_t onceToken; + (instancetype)shareInstance { dispatch_once(&onceToken, ^{ shareInstance = [[self alloc] init]; }); return shareInstance; } - (instancetype)init { self = [super init]; if (self) {} return self; } @end

    单例的关键字static,被它修饰的对象,存储在静态存储区,会在当前内存中保持唯一性和持久性(对象会在程序的整个运行期间都存在);上面的单例写法是最为常见的做法,dispatch_once保证了线程安全性,但是如果要保证真正的唯一性,还不够。

    二、“严格”的单例

    + (id)allocWithZone:(NSZone *)zone { if(!shareInstance) { shareInstance = [super allocWithZone:zone]; } return shareInstance; } - (id)copy { return shareInstance; } - (id)mutableCopy { return shareInstance; }

    创建对象的步骤分为两步:(1)申请内存(alloc);(2)初始化(init) 这两个步骤;

    因为外部的操作是不可控的,为了保证严格的唯一性,因此在第一步这个阶段我们就要拦截它;当我们调用alloc方法时,OC内部会调用allocWithZone这个方法来申请内存,我们覆写这个方法,然后在这个方法中调用shareInstance方法返回单例对象,这样就可以达到我们的目的。

    拷贝对象也是同样的原理,覆写copy方法,然后在这个方法中调用shareInstance方法返回单例对象。

    这样以来:

    MDInstanceManager *instanceManager = [MDInstanceManager shareInstance]; NSLog(@"-->%@",instanceManager); NSLog(@"-->%@",[[MDInstanceManager alloc]init]); NSLog(@"-->%@",[instanceManager copy]); NSLog(@"-->%@",[instanceManager mutableCopy]); 2020-06-30 11:21:53.886952+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070> 2020-06-30 11:21:53.887055+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070> 2020-06-30 11:21:56.200656+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070> 2020-06-30 11:21:56.840126+0800 MDProject[4723:1906563] --><MDInstanceManager: 0x600002455070>

    以上四种操作的内存地址都是一样的;

    当然你也可以采取更为粗暴的方法,如下:

    - (instancetype)init __attribute__((unavailable("replace with 'sharedInstance'"))); + (instancetype)alloc __attribute__((unavailable("replace with 'sharedInstance'"))); + (instancetype)new __attribute__((unavailable("replace with 'sharedInstance'"))); - (instancetype)copy __attribute__((unavailable("replace with 'sharedInstance'"))); - (instancetype)mutableCopy __attribute__((unavailable("replace with 'sharedInstance'")));

    三、单例优缺点

    优点:

    提供了应用唯一的实例对象,规范化统一管理资源,即提供了对唯一实例的受控访问; 

    不用再频繁地创建和销毁对象,从而提高了系统的性能和节约系统资源;

    单例对象可以做到按需创建对象或加载资源,以节省不必要的内存;

    避免对共享资源的多重占用;

    缺点:

    单例从创建后到彻底关闭程序前都会一直存在,如果过多的创建单例无疑浪费系统资源和影响系统效率;

    由于单例模式中没有抽象层接口,因此单例类很难再进行扩展;

    单例类的职责过重,在一定程度上违背了“单一职责原则”,长期的累积会导致难以维护;

    因此可以把单例模式作为最后的兜底方案考虑,不宜过多使用;

    四、单例的销毁

    单例是可以销毁的,如下:

    - (void)destroy { shareInstance = nil; onceToken = 0; }

    五、Weak单例

    static MDInstanceManager *shareInstance = nil; 以上static修饰的默认用一个强指针来持有这个单例,如果当前业务场景退出后,对应的单例管理对象,也没有用了,当然,也可以手动释放这个单例对象,但是业务使用中,需要注意的是释放的时机; static __weak MDWeakInstanceManager *weakInstance = nil; 但是,如果改用OC特有的weak修饰,让对应的VC去持有它,那么这个weak单例就会自动伴随对应的VC释放而释放,而不需要我们去手动在干预;对于复杂的业务场景,我们常常需要一个管理类来作为中间者,weak单例无疑是一种很好的选择。 为了规范化这个过程,在实际开发中,我定义了一个protocol才实现VC对weak单例的持有,让对应的持有者去实现它,如下: @protocol MDWeakInstanceManagerDelegate <NSObject> - (void)assignInstance:(MDWeakInstanceManager *)instance; @end

    持有者VC的实现代码如下:

    @interface MDWeakInstanceViewController ()<MDWeakInstanceManagerDelegate> @property (nonatomic, strong) MDWeakInstanceManager *weakInstanceManager; @end @implementation MDWeakInstanceViewController - (void)viewDidLoad { [super viewDidLoad]; [MDWeakInstanceManager buildInstance:self]; } #pragma mark -- MDWeakInstanceManagerDelegate - (void)assignInstance:(MDWeakInstanceManager *)instance { self.weakInstanceManager = instance; } @end

    单例中实现如下:

    static __weak MDWeakInstanceManager *weakInstance = nil; + (void)buildInstance:(id)delegate; { MDWeakInstanceManager *strongInstance = weakInstance; @synchronized(self) { if (!strongInstance) { strongInstance = [[[self class] alloc] init]; weakInstance = strongInstance; } } strongInstance.delegate = delegate; if (strongInstance.delegate && [strongInstance.delegate respondsToSelector:@selector(assignInstance:)]) { [strongInstance.delegate assignInstance:strongInstance]; } } //访问时须用此方法 + (MDWeakInstanceManager *)shareInstance { return weakInstance; }

    这样我们在持有者VC的业务场景中,都可以使用 [MDWeakInstanceManager shareInstance],而一旦它的持有者VC释放了,[MDWeakInstanceManager shareInstance]也会自动变为nil,无需手动干涉;

     

    参考文献

    https://www.cnblogs.com/NerdFooProgrammer/p/4870260.html

    https://www.yunbook.vip/post/1547606575302.html

    http://www.cocoachina.com/articles/19857

     

    请关注公众号:

    Processed: 0.012, SQL: 9