美文网首页
iOS底层原理 - 单例的正确写法

iOS底层原理 - 单例的正确写法

作者: hazydream | 来源:发表于2020-05-19 10:33 被阅读0次

    面试题引发的思考:

    Q: 介绍单例及其用途?

    • 单例模式保证系统中 一个类 只有 一个实例 而且该实例 易于外界访问
    • 只初始化一次,生命周期 和 程序的生命周期 相同,访问方便;
    • 主要用来封装网络请求、播放器、存放常用数据等。

    Q: 单例正确写法?

    • OC正确写法如下:
    // TODO: -----------------  Singleton类  -----------------
    @interface Singleton : NSObject <NSCopying, NSMutableCopying>
    + (instancetype)sharedInstance;
    - (void)justdoit;
    @end
    
    @implementation Singleton
    // 初始化方法dispatch_once,本身是线程安全的,保证整个程序中只会执行一次
    + (instancetype)sharedInstance  {
        static Singleton *instance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[super allocWithZone:nil] init];
        });
        return instance;
    }
    // 重写 allocWithZone
    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        return [Singleton sharedInstance];
    }
    // 遵从NSCopying协议,可通过copy方式创建对象
    - (id)copyWithZone:(NSZone *)zone {
        return self;
    }
    // 遵从NSMutableCopying协议,可通过mutableCopy方式创建对象
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return self;
    }
    // 实例方法
    - (void)justdoit {
        NSLog(@"just do it");
    }
    @end
    
    • Swift正确写法如下:
    // TODO: -----------------  Singleton类  -----------------
    class Singleton {
        static let shared = Singleton()
        // 私有化构造器
        private init() { }
        // 方法
        func justdoit() {
            print("just do it")
        }
    }
    

    1. 单例

    (1) 单例模式

    单例模式保证系统中 一个类 只有 一个实例 而且该实例 易于外界访问

    • 只初始化一次,生命周期 和 程序的生命周期 相同,访问方便;
    • 主要用来封装网络请求、播放器、存放常用数据等。

    经常见到的一些系统单例:

    [UIApplication sharedApplication]:应用程序
    [NSNotificationCenter defaultCenter]:通知
    [NSUserDefaults standardUserDefaults]:本地化存储
    [NSFileManager defaultManager]:文件操作


    (2) 单例写法

    苹果官方推荐写法:

    // TODO: -----------------  Singleton类  -----------------
    @interface Singleton : NSObject
    + (instancetype)sharedInstance;
    - (void)justdoit;
    @end
    
    @implementation Singleton
    + (instancetype)sharedInstance {
        // 声明一个 空的静态的 单例对象
        static Singleton *instance = nil;
        // 声明一个 静态的 gcd的单次任务
        static dispatch_once_t onceToken;
        // 执行gcd单次任务
        dispatch_once(&onceToken, ^{
            // 对对象进行初始化
            instance = [[Singleton alloc] init];
        });
        return instance;
    }
    - (void)justdoit {
        NSLog(@"just do it");
    }
    @end
    

    通过[Singleton sharedInstance]创建对象,即可调用相关方法:

    // TODO: -----------------  ViewController类  -----------------
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        /// 创建单例
        Singleton *singleton = [Singleton sharedInstance];
        [singleton justdoit];
    }
    @end
    
    // 打印结果
    Demo[1234:567890] just do it
    

    (3) 以上写法问题

    如果不通过[Singleton sharedInstance]创建对象,而是通过alloc或者new创建对象,会出现什么问题呢?

    // TODO: -----------------  ViewController类  -----------------
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        /// 通过不同方式创建对象
        Singleton *singletonA= [Singleton sharedInstance];
        Singleton *singletonB= [Singleton sharedInstance];
        Singleton *singletonC = [[Singleton alloc] init];
        Singleton *singletonD = [Singleton new];
    
        NSLog(@"singletonA - %@", singletonA);
        NSLog(@"singletonB - %@", singletonB);
        NSLog(@"singletonC - %@", singletonC);
        NSLog(@"singletonD - %@", singletonD);
    }
    @end
    
    // 打印结果
    Demo[1234:567890] singletonA - <Singleton: 0x283d35290>
    Demo[1234:567890] singletonB - <Singleton: 0x283d35290>
    Demo[1234:567890] singletonC - <Singleton: 0x283d352a0>
    Demo[1234:567890] singletonD - <Singleton: 0x283d352b0>
    

    由以上结果可知:

    实例对象singletonAsingletonB的地址值是相同的;而此两者与实例对象singletonCsingletonD的地址值是不同的。

    说明此情况下:

    通过[Singleton sharedInstance]只会创建一次对象;而通过alloc或者new会创建新的对象。

    因为单例模式保证系统中一个类只有一个实例,所以以上单例的写法不够严谨,会出现问题。


    2. 单例的正确写法

    (1) 正确写法一

    在创建对象的时候,alloc或者new都会调用到allocWithZone:方法,需要重写 allocWithZone:方法。

    如果调用了copymutableCopy方法就会导致程序运行崩溃,需要实现copymutableCopy就要遵循协议实现方法。

    // TODO: -----------------  Singleton类  -----------------
    @interface Singleton : NSObject <NSCopying, NSMutableCopying>
    + (instancetype)sharedInstance;
    - (void)justdoit;
    @end
    
    @implementation Singleton
    // 初始化方法dispatch_once,本身是线程安全的,保证整个程序中只会执行一次
    + (instancetype)sharedInstance  {
        static Singleton *instance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[super allocWithZone:nil] init];
        });
        return instance;
    }
    // 重写 allocWithZone
    + (instancetype)allocWithZone:(struct _NSZone *)zone {
        return [Singleton sharedInstance];
    }
    // 遵从NSCopying协议,可通过copy方式创建对象
    - (id)copyWithZone:(NSZone *)zone {
        return self;
    }
    // 遵从NSMutableCopying协议,可通过mutableCopy方式创建对象
    - (id)mutableCopyWithZone:(NSZone *)zone {
        return self;
    }
    // 实例方法
    - (void)justdoit {
        NSLog(@"just do it");
    }
    @end
    

    通过各种方式创建对象,查看打印结果:

    // TODO: -----------------  ViewController类  -----------------
    @implementation ViewController
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        Singleton *singletonA= [Singleton sharedInstance];
        Singleton *singletonB= [Singleton sharedInstance];
        Singleton *singletonC = [[Singleton alloc] init];
        Singleton *singletonD = [Singleton new];
        Singleton *singletonE = [singletonA copy];
        Singleton *singletonF = [singletonC mutableCopy];
    
        NSLog(@"singletonA - %@", singletonA);
        NSLog(@"singletonB - %@", singletonB);
        NSLog(@"singletonC - %@", singletonC);
        NSLog(@"singletonD - %@", singletonD);
        NSLog(@"singletonE - %@", singletonE);
        NSLog(@"singletonF - %@", singletonF);
    }
    @end
    
    // 打印结果
    Demo[1234:567890] singletonA - <Singleton: 0x2815b9310>
    Demo[1234:567890] singletonB - <Singleton: 0x2815b9310>
    Demo[1234:567890] singletonC - <Singleton: 0x2815b9310>
    Demo[1234:567890] singletonD - <Singleton: 0x2815b9310>
    Demo[1234:567890] singletonE - <Singleton: 0x2815b9310>
    Demo[1234:567890] singletonF - <Singleton: 0x2815b9310>
    

    由打印结果可知:

    实例对象singletonA~singletonF的地址值是相同的。

    说明此情况下:

    通过各种方式创建对象,而对象只会创建一次,符合单例模式定义。


    (2) 正确写法二

    设置只能通过[Singleton sharedInstance]创建对象,直接禁用allocnewcopymutableCopy等方法即可:

    // TODO: -----------------  Singleton类  -----------------
    @interface Singleton : NSObject
    + (instancetype)sharedInstance;
    - (void)justdoit;
    
    + (instancetype)alloc NS_UNAVAILABLE;
    + (instancetype)new NS_UNAVAILABLE;
    - (instancetype)copy NS_UNAVAILABLE;
    - (instancetype)mutableCopy NS_UNAVAILABLE;
    @end
    
    @implementation Singleton
    + (instancetype)sharedInstance {
        static Singleton *instance = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            instance = [[Singleton alloc] init];
        });
        return instance;
    }
    - (void)justdoit {
        NSLog(@"just do it");
    }
    @end
    

    此时无法通过allocnewcopymutableCopy等方法创建对象,调用会报错:

    报错

    (3) Swift单例正确写法

    Swift中创建单例很方便:

    // TODO: -----------------  Singleton类  -----------------
    class Singleton {
        static let shared = Singleton()
        // 私有化构造器
        private init() { }
    
        func justdoit() {
            print("just do it")
        }
    }
    
    // TODO: -----------------  ViewController类  -----------------
    class ViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any setup after loading the view.
            let singleton = Singleton.shared
            singleton.justdoit()
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS底层原理 - 单例的正确写法

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