iOS设计模式浅析之单例模式(Singleton)

  • 1.1 概念

(1)单例模式

(1.1)在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
(1.2)方便地控制了实例个数,并节约系统资源
(1.3)单例是不可以用继承的,因为不能父类与子类共用一个全局变量

(2)使用场合

在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次);
可以理解为当需要一个24小时响应的“马仔”帮你实现某些功能时,使用单例。

(3)约束-不要滥用单例模式

1. 单例是一种全局状态,意味着App的任何地方都可以和这个单例进行交互,无形中增加了代码的耦合
2. 单例的生命周期难以控制,虽然可以在单例中提供方法设为nil进行释放。
  • 1.2 实现单例(在ARC与MRC中均可执行)

(1)实现步骤

01 在类的内部提供一个static修饰的全局变量
02 重写+allocWithZone方法,保证永远都只为单例对象分配一次内存空间
03 提供一个类方法,方便外界访问
04 严谨起见,重写-copyWithZone方法和-MutableCopyWithZone方法
05 通过条件编译判断是否是ARC{__has_feature(objc_arc)},若是,则不再执行代码,否则执行下述06-09代码
06 重写release方法
07 重写retain方法
08 建议在retainCount方法中返回一个最大值
09 结束条件编译

(2)如果在MRC中运行,需配置MRC环境知识

01 注意ARC不是垃圾回收机制,是编译器特性
02 配置MRC环境:build setting ->搜索automatic ref->修改为NO

(3)相关代码

  • allocWithZone方法中加锁或GCD一次性代码详见多线程
    //提供一个static修饰的全局变量,强引用着已经实例化的单例对象实例
    static id _instance;
    
    //保证永远只分配一次存储空间
    +(instancetype)allocWithZone:(struct _NSZone *)zone
    {
        //使用GCD中的一次性代码
    //    static dispatch_once_t onceToken;
    //    dispatch_once(&onceToken, ^{
    //        _instance = [[super allocWithZone:zone]init];
    //    });
    
        //使用加锁的方式,保证只分配一次存储空间
        @synchronized(self) {
            if (_instance == nil) {
                _instance = [super allocWithZone:zone];
            }
        }
        return _instance;
    }
    
    //类方法,返回一个单例对象
    +(instancetype)shareTools
    {
         //注意:这里建议使用self,而不是直接使用类名Tools(考虑继承)
    
        return [[self alloc]init];
    }
    
    //让代码更加的严谨
    -(nonnull id)copyWithZone:(nullable NSZone *)zone
    {
    //    return [[self class] allocWithZone:zone];
        return _instance;
    }
    
    -(nonnull id)mutableCopyWithZone:(nullable NSZone *)zone
    {
        return _instance;
    }
    
    #if __has_feature(objc_arc)
        //如果是ARC环境下
    #else
        //如果是MRC环境下
    //在MRC环境下,如果用户retain了一次,那么直接返回instance变量,不对引用计数器+1
    //如果用户release了一次,那么什么都不做,因为单例模式在整个程序运行过程中都拥有且只有一份,程序退出之后被释放,所以不需要对引用计数器操作
    -(oneway void)release
    {
    }
    
    -(instancetype)retain
    {
        return _instance;
    }
    
    //惯用法,有经验的程序员通过打印retainCount这个值可以猜到这是一个单例
    -(NSUInteger)retainCount
    {
        return MAXFLOAT;
    }
    #endif
    

单例封装宏

01 注意条件编译的代码不能包含在宏定义里面
02 宏定义的代码只需要写一次就好,之后直接拖到项目中用就OK
  • .h文件
    #define SingleH(name) +(instancetype)share##name;
  • .m文件
    #if __has_feature(objc_arc)
//ARC
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
    static dispatch_once_t onceToken;\
    dispatch_once(&onceToken, ^{\
        _instance = [super allocWithZone:zone];\
    });\
    return _instance;\
}\
\
+(instancetype)share##name\
{\
    return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
    return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
    return _instance;\
}

#else
//mrc
#define SingleM(name) static id _instance;\
+(instancetype)allocWithZone:(struct _NSZone *)zone\
{\
static dispatch_once_t onceToken;\
dispatch_once(&onceToken, ^{\
_instance = [super allocWithZone:zone];\
});\
return _instance;\
}\
\
+(instancetype)share##name\
{\
return [[self alloc]init];\
}\
\
-(id)copyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
\
-(id)mutableCopyWithZone:(NSZone *)zone\
{\
return _instance;\
}\
-(oneway void)release\
{\
}\
\
-(instancetype)retain\
{\
    return _instance;\
}\
\
-(NSUInteger)retainCount\
{\
    return MAXFLOAT;\
}

#endif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容