【iOS】单例模式

【iOS】单例模式

前言

单例模式是很重要的一部分内容,这里重点总结复习一下。

定义

单例模式是一种设计模式,保证一个类在程序运行中只会有一个实例,并且提供全局访问点。这样防止一个实例被重复创建而占用内存空间,大大节省了内存。

优缺点

优点

  • 一个类只被实例化了一次,提供了对唯一实例的受控访问
    • 保证所有人拿到的是同一个对象
    • 保证在需要时才初始化,也就是之前博客中提到的懒加载
objc 复制代码
Person *p1 = [[Person alloc] init];
Person *p2 = [[Person alloc] init];
NSLog(@"%p %p", p1, p2);
//每次alloc init,会输出不同地址
objc 复制代码
MyManager *m1 = [MyManager sharedManager];
MyManager *m2 = [MyManager sharedManager];
NSLog(@"%p %p", m1, m2); 
//避免重复创建,输出的是相同地址
  • 节省系统资源

缺点

  • 全局状态:单例模式可能导致全局状态存在,就是程序不同模块都可能修改这个单例对象的状态,增加代码复杂性,且bug不好定位。

  • 难以扩展:单例模式的实例是固定的,导致很难扩展以支持多个实例。

  • 不适用于多线程环境:在多线程环境下,需要处理保证初始化代码只执行一次,而且线程是安全的。

    • 互斥锁:
    objc 复制代码
    + (instancetype)sharedInstance {
        @synchronized (self) {
            if (_instance == nil) {
                _instance = [[self alloc] init];
            }
        }
        return _instance;
    }
    • dispatch_once
    objc 复制代码
    + (instancetype)sharedInstance {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _instance = [[self alloc] init];
        });
        return _instance;
    }

种类

懒汉式

主要指在需要时才会创建单例对象的实例,而不是在程序启动时就立即创建(懒加载)。懒汉式通过延迟对象的初始化来节省资源和提高性能。

这里展示两种写法:

  • 双重检查锁
objc 复制代码
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

static id _instance;

+(instancetype)allocWithZone:(struct _NSZone *)zone {
    if (_instance == nil) {
        @synchronized (self) {
            if (_instance == nil) {
                _instance = [super allocWithZone:zone];
            }
        }
    }
    return _instance;
}

+(instancetype)shareSingleton {
    if (_instance == nil) {
        @synchronized (self) {
            if (_instance == nil) {
                _instance = [[self alloc] init];
            }
        }
    }
    return _instance;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    _instance = [[NSString alloc] init];
    _instance = @"单例模式";
    NSLog(@"%@", _instance);
}

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

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

@end

这里详细解释一下这种写法中几个重要的需要注意的点:

  1. static id _instance:static用于保存类的唯一实例,保证进程中只有一份,更具体的将在单独写篇博客来比较static、const、extern关键字。
  2. 重写allocWithZone::双重检查锁来保证线程安全,当外部调用alloc init时,保证始终只分配一次内存。
  3. copyWithZone:mutableCopyWithZone::用于复制对象的方法,它们都被重写后返回同一个_instance,保证不会生成新对象,仍然符合单例模式的要求。
  • GCD写法
objc 复制代码
#import "sharedSingleton.h"

static id _instance = nil;

@implementation sharedSingleton

+(instancetype)sharedSingleton {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init];
    });
    return _instance;
}

+(instancetype)allocWithZone:(struct _NSZone *)zone {
    return [sharedSingleton sharedSingleton];;
}

-(id)copyWithZone:(NSZone *)zone {
    return [sharedSingleton sharedSingleton];
}

-(id)mutableCopyWithZone:(NSZone *)zone {
    return [sharedSingleton sharedSingleton];
}

@end

dispatch_once主要是根据onceToken的值来决定怎么去执行代码:

  1. 当onceToken = 0时,线程执行block中的代码
  2. 当onceToken = -1时,线程跳过block中的代码不执行
  3. 当onceToken = 其他值时,线程被阻塞,等待onceToken值改变

饿汉式

饿汉式创建单例是在你的类加载时立即开始加载一个单例的一种单例模式,这种模式需要我们把加载单例的代码写在类第一次加载的位置。

objc 复制代码
#import "ehanSingleton.h"

static id _instance;

@implementation ehanSingleton

//当类加载到OC运行时的环境内存中调用
+(void)load {
    _instance = [[self alloc] init];
}

+(instancetype)ehanSingleton {
    return _instance;
}

+(instancetype)allocWithZone:(struct _NSZone *)zone {
    if (!_instance) {
        _instance = [super allocWithZone:zone];
    }
    return _instance;
}

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

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

@end

优缺点比较

  • 懒汉式:
    • 优点:
      • 懒加载:只有在第一次访问单例实例的时候才会被创建,这样可以节省资源,提高性能。
      • 线程安全:通过GCD或加锁来保证线程安全性。
      • 节省内存:如果单例对象很大或者初始化过程开销较大,懒汉模式可避免在程序启动时就创建不必要的对象。
    • 缺点:
      • 线程安全性的开销:为保证线程安全的操作带来性能上的开销。
  • 饿汉式:
    • 优点:
      • 创建方式简单:不出现多线程的问题。
      • 线程安全:实例在类创建时就创建,不会存在多个实例的风险,保证线程安全。
    • 缺点:
      • 性能问题:如果单例类的实例在启动程序时没有被使用,那么创建实例的开销可能是不必要的。
      • 无法实现延迟加载:饿汉模式在程序启动时就创建实例,无法实现延迟加载,可能会浪费资源,尤其是当实例很大或初始化开销较大时。

总结

总的来说,懒汉模式适用于需要延迟加载实例的情况,饿汉模式适用于需要简单实现和线程安全性的情况,具体情况具体分析。

相关推荐
游戏开发爱好者81 小时前
iOS 开发者的安全加固工具,从源码到成品 IPA 的多层防护体系实践
android·安全·ios·小程序·uni-app·cocoa·iphone
编程小Y2 小时前
Redux在iOS中的使用
ios
游戏开发爱好者84 小时前
Charles 抓不到包怎么办?从 HTTPS 代理排错到底层数据流补抓的完整解决方案
网络协议·http·ios·小程序·https·uni-app·iphone
1024小神4 小时前
在 Swift 中,self. 的使用遵循明确的规则
开发语言·ios·swift
Swift社区4 小时前
Swift 类型系统升级:当协议遇上不可拷贝的类型
开发语言·ios·swift
小小8程序员9 小时前
swift的inout的用法
开发语言·ios·swift
JZXStudio10 小时前
独立开发者亲测:MLX框架让我的App秒变AI原生!15年iOS老兵的2025新感悟
前端·ios
南玖i11 小时前
vue2/html 实现高德点聚合
开发语言·ios·swift