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

总结

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

相关推荐
马拉萨的春天21 小时前
iOS的动态库和静态库的差异区别以及静态库的好处
macos·ios·cocoa
肖老师xy21 小时前
苹果(IOS)制作开发和发布证书
ios
马拉萨的春天1 天前
探索Objective-C中的对象复制:深入理解copy和mutableCopy
开发语言·ios·objective-c
00后程序员张1 天前
Fiddler使用教程,全面掌握Fiddler抓包工具的配置方法、代理设置与调试技巧(HTTPHTTPS全解析)
前端·测试工具·ios·小程序·fiddler·uni-app·webview
2501_916008891 天前
HTTPS 下的 DDoS 防护与抓包分析实战,从检测到快速缓解的工程化打法
网络协议·ios·小程序·https·uni-app·iphone·ddos
2501_915918411 天前
App 使用 HTTPS 的工程化实战,从接入到真机排查的一线指南
android·ios·小程序·https·uni-app·iphone·webview
hookserver1 天前
企业微信ipad协议接口优势
http·ios·微信·企业微信·ipad·企微
Digitally1 天前
如何从iPhone提取照片:2025年的6种方法
ios·iphone
代码s贝多芬的音符1 天前
ios android 小程序 蓝牙 CRC16_MODBUS
android·ios·小程序
非专业程序员Ping1 天前
从0到1自定义文字排版引擎:原理篇
ios·swift·assembly·font