美文网首页
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底层原理 - 单例的正确写法

    面试题引发的思考: Q: 介绍单例及其用途? 单例模式保证系统中 一个类 只有 一个实例 而且该实例 易于外界访问...

  • iOS 单例模式

    关于单例模式的详解,看完这几篇,就完全了然了。iOS 单例模式iOS中的单例模式iOS单例的写法

  • iOS 单例的正确写法

    单例是 iOS 设计模式之一 1、大家一般都会这么写 一般情况下,初始化一个类 会使用 init,假如使用 ini...

  • Android中常见的内存泄漏汇总

    1.单例模式的错误写法 单例模式的正确写法: 2.非静态内部类创建静态实例造成的内存泄漏错误写法 正确写法:将该内...

  • 单例的2种写法

    单例模式是iOS开发中最常用的设计模式,iOS的单例模式有两种官方写法,如下: 1,常用写法 import "Se...

  • iOS单例模式的正确写法

    单例模式很常见,但是,能真正把单利模式写对的却很少。在iOS中,一般我们都是用官方推荐的写法来写单例: URLMa...

  • iOS 单例模式的正确写法

    大家平时写单例的时候可能没注意到,如果别人init了这个类,就会创建一个新的对象,要保证永远都只为单例对象分配一次...

  • iOS-正确单例的写法

    前言 单例应该是iOS中很简单的设计模式,写个单例很简单很方便。网上例子也很多,大家也是基本上copy下来就可以了...

  • iOS-单例的正确写法

    一、OC 中创建单例的几种方式 1.1 单线程模式单例 存在的问题就是: 只能在单线程中使用, 一旦有多线程同时调...

  • iOS-两种单例模式的实现

    单例模式是开发中最常用的写法之一,创建一个单例很多办法,iOS的单例模式有两种官方写法,如下: 不使用GCD 某些...

网友评论

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

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