美文网首页
iOS开发之单例模式(Singleton Pattern)

iOS开发之单例模式(Singleton Pattern)

作者: _Joeyoung_ | 来源:发表于2018-04-03 18:19 被阅读40次

    这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
    这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

    注意:

    • 1、单例类只能有一个实例。
    • 2、单例类必须自己创建自己的唯一实例。
    • 3、单例类必须给所有其他对象提供这一实例。

    iOS 中单例模式的实现方式一般分为两种:Non-ARC(非 ARC)和 ARC+GCD。

    建模:

    NON-ARC(非 ARC)的实现:

    可查看官方文档:Creating a Singleton Instance
    因为现在iOS的项目默认是ARC的,我们需要将其对应的文件转换为非arc模式,如下图:

    Step 1:

    创建单例类 NonARCSingleton

    Step 2:

    声明一个静态单例对象并初始化为nil.

    // .m
    static NonARCSingleton *instanceObj = nil;
    

    Step 3:

    .m 中实现在头文件中定义的方法 + (instancetype)sharedInstance;

    // 获取一个 sharedInstance 实例,如果有必要的话,实例化一个
    + (instancetype)sharedInstance {
        if (instanceObj == nil) {
            instanceObj = [[super allocWithZone:NULL] init];
        }
        return instanceObj;
    }
    // 当第一次使用这个单例时,会调用这个 init 方法。
    - (id)init {
        self = [super init];
        if (self) {
            // 通常在这里做一些相关的初始化任务
            
        }
        return self;
    }
    

    Step 4:

    重写 allocWithZone: 方法,防止他人直接通过分配内存初始化的方式生成新的对象。

    // 通过返回当前的 sharedInstance 实例,就能防止实例化一个新的对象。
    + (id)allocWithZone:(NSZone*)zone {
        return [[self sharedInstance] retain];
    }
    

    Step 5:

    实现基本协议的方法 copyWithZone:mutableCopyWithZonereleaseretainretainCountautorelease 并做适当的事情,以确保单例状态。

    // 不希望生成单例的多个拷贝
    - (id)copyWithZone:(NSZone *)zone {
        return self;
    }
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return self;
    }
    
    // 什么也不做 -- 该单例并不需要一个引用计数(retainCount)
    - (id)retain {
        return self;
    }
    
    // 替换掉引用计数 -- 永远都不会 release 这个单例。
    - (NSUInteger)retainCount {
        return NSUIntegerMax;
    }
    
    // 该方法是空的 -- 不希望用户 release 掉这个对象。
    - (oneway void)release {
    }
    
    // 除了返回单例外,什么也不做。
    - (id)autorelease {
        return self;
    }
    
    // 这个 dealloc 方法永远都不会被调用 -- 因为在程序的生命周期内容,该单例一直都存在。(所以该方法可以不用实现)
    -(void)dealloc
    { 
        [super dealloc];
    }
    

    Step 6:测试

    NonARCSingleton *singleton1 = [NonARCSingleton sharedInstance];
    NSLog(@"singleton1对象的内存地址为:%p",singleton1);
    NonARCSingleton *singleton2 = [[NonARCSingleton alloc] init];
    NSLog(@"singleton2对象的内存地址为:%p",singleton2);
    NonARCSingleton *singleton3 = [singleton2 copy];
    NSLog(@"singleton3对象的内存地址为:%p",singleton3);
    NonARCSingleton *singleton4 = [singleton2 mutableCopy];
    NSLog(@"singleton4对象的内存地址为:%p",singleton4);
    

    控制台打印效果:

    打印结果一目了然,生成的四个对象指向同一块内存地址,单例对象创建成功了!!!


    上面使用非ARC实现单例的方法并不是线程安全的,如果有多个线程调用 sharedInstance 方法,很可能造成 init 方法多次调用,在不同线程中获取的不是同一个实例

    解决方法:使用 @synchronized 来创建互斥锁 或者 dispatch_once_t。
    // 保证在实例化的时候是线程安全的
    // (当然,该方法不能保证该单例中所有方法的调用都是线程安全的)
    @synchronized (self) {
        if (instanceObj == nil) {
            instanceObj = [[super allocWithZone:NULL] init];
        }
    }
    

    提醒:在 iOS 中,一般不建议使用非 ARC 来实现单例模式。更好的方法是使 用 ARC+GCD 来实现。


    ARC的实现:

    Step 1:

    创建单例类 ARCSingleton

    Step 2:
    声明一个静态单例对象并初始化为nil.

    static ARCSingleton *_instanceObj = nil;
    

    Step 3:

    .m 中实现在头文件中定义的方法 + (instancetype)sharedInstance;

    + (instancetype)sharedInstance {
        // 只会执行一次
        // 该方法是线程安全的
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (!_instanceObj) {
                _instanceObj = [[self alloc] init];
            }
        });
        return _instanceObj;
    }
    
    // 当第一次使用这个单例时,会调用这个 init 方法
    - (id)init {
        self = [super init];
        if (self) {
            // 通常在这里做一些相关的初始化任务
            
        }
        return self;
    }
    

    Step 4:

    重写 allocWithZone: 方法,防止他人直接通过分配内存初始化的方式生成新的对象。

    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            if (!_instanceObj) {
                _instanceObj = [super allocWithZone:zone];
            }
        });
        return _instanceObj;
    }
    

    Step 5:

    实现基本协议的方法 copyWithZone:mutableCopyWithZone,防止他人通过拷贝的方式生成新的对象。

    - (id)copyWithZone:(NSZone *)zone {
        return self;
    }
    
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return self;
    }
    

    Step 6:测试

    ARCSingleton *singleton1 = [ARCSingleton sharedInstance];
    NSLog(@"singleton1对象的内存地址为:%p",singleton1);
    ARCSingleton *singleton2 = [[ARCSingleton alloc] init];
    NSLog(@"singleton2对象的内存地址为:%p",singleton2);
    ARCSingleton *singleton3 = [singleton2 copy];
    NSLog(@"singleton3对象的内存地址为:%p",singleton3);
    ARCSingleton *singleton4 = [singleton2 mutableCopy];
    NSLog(@"singleton4对象的内存地址为:%p",singleton4);
    

    控制台打印效果:

    打印结果一目了然,生成的四个对象指向同一块内存地址,使用ARC创建单例对象成功了!!!


    我把单例模式写成了宏 Singleton.h ,方便使用。

    使用步骤如下:

    Step 1:导入头文件

    #import "Singleton.h"
    

    Step 2:在 .h.m 文件中实现宏

    // .h 
    #import <Foundation/Foundation.h>
    #import "Singleton.h"
    
    @interface MacroSingleton : NSObject
    // ()中为当前的类名
    singleton_h(MacroSingleton);
    
    @end
    
    // .m 
    #import "MacroSingleton.h"
    
    @implementation MacroSingleton
    
    singleton_m(MacroSingleton);
    
    @end
    
    

    Step 3:使用方法

    MacroSingleton *singleton1 = [MacroSingleton shared];
    

    (完)


    代码放在GitHub上了,有需要查看的可以 ->戳这里。


    千里之行,始于足下。

    相关文章

      网友评论

          本文标题:iOS开发之单例模式(Singleton Pattern)

          本文链接:https://www.haomeiwen.com/subject/attchftx.html