iOS [Objective-C] ARC 单例模式

2016-12-20  本文已影响19人  巴糖

概念

在程序运行过程,一个类只有一个实例

使用场合

在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)

ARC实现单例

  1. 在类的内部提供一个static修饰的全局变量
  2. 提供一个类方法,方便外界访问
  3. 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间
  4. 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法

源码

<NSCopying>的协议,并实现相应的方法,防止别人误调copy方法而崩溃

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface CustomManager : NSObject

+ (instancetype)new NS_UNAVAILABLE;

//命名规范 share+类名 | default+类名 |  类名
+ (instancetype)shareCustomManager;


@end

NS_ASSUME_NONNULL_END
@interface CustomManager ()<NSCopying>

@end

@implementation CustomManager

// 提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
static CustomManager *_manager;

// 类方法,返回一个单例对象
+(instancetype)shareCustomManager
{
    // 注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
    return [[self alloc]init];
}

// new -> alloc -> allocWithZone 最终必须调用的方法
// 保证永远只分配一次存储空间
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
    // 使用GCD中的一次性代码
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [super allocWithZone:zone];
    });
    
    // 使用加锁的方式,保证只分配一次存储空间
    @synchronized(self) {
        if (_manager == nil) {
            _manager = [super allocWithZone:zone];
        }
    }
    return _manager;
}

// 1. mutableCopy 创建一个新的可变对象,并初始化为原对象的值,新对象的引用计数为 1;
// 2. copy 返回一个不可变对象。分两种情况:
//(1)若原对象是不可变对象,那么返回原对象,并将其引用计数加1;
//(2)若原对象是可变对象,那么创建一个新的不可变对象,并初始化为原对象的值,新对象的引用计数为 1。
//让代码更加的严谨
-(nonnull id)copyWithZone:(nullable NSZone *)zone
{
    return [[self class] allocWithZone:zone];
    return _manager;
}

-(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
{
    return _manager;
}
@end

单利创建的几种方式

1、传统方法

//传统方式
+(instancetype)traditionSingleton{
    static CustomManager *singleton = nil;
    if (singleton == nil){
        singleton = [[CustomManager alloc] init];
    }
    return singleton;
}

2、GCD方式

//GCD方式
+(instancetype)gcdSingleton{
    static CustomManager *singleton = nil;
    //给单例加了一个线程锁
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        singleton = [[CustomManager alloc] init];
    });
    return singleton;
}

3、加锁方式

//加锁方式
+(CustomManager*)singleton {
    static CustomManager* singleton;
    @synchronized(self) {
        if (!singleton) {
            singleton = [[CustomManager alloc] init];
        }
        return singleton;
    }
    return nil;
}

扩展

Q:new -> alloc -> allocWithZone 最终必须调用的方法
A: 通过打印方式查看调用方法;
+[CustomManager new]
+[CustomManager alloc]
+[CustomManager allocWithZone:]

说明

避免使用new创建对象,从安全和设计角度来说我们应该对初始化所有属性,提高程序的健壮性和复用性。
不论是何种情况,在类中至少包含一个构造函数是一种很好的编程实践,如果类中有属性,好的实践往往是初始化这些属性。
——以上摘自《The Object-Oriented Thought Process》 by Matt Weisfeld

更新记录

版本 时间
2.1 2019年01月30日14:29:26
2.0 2018年12月25日11:18:02
上一篇下一篇

猜你喜欢

热点阅读