文章目录

  • 前言
  • 一、什么是单例模式
  • 二、单例模式的优缺点
    • 优点
    • 缺点
  • 三、模式介绍
    • 1.懒汉模式(GCD & 互斥锁)
      • GCD 写法
      • 互斥锁写法(双重检查锁)
    • 2.饿汉模式
  • 总结
      • 懒汉式 + 互斥锁(Mutex)
      • **懒汉式 + GCD (dispatch_once) **
      • 饿汉式

前言

这篇博客是对单例模式的一个深入了解,之前的对单例模式的学习太过浅薄了,所以撰写这篇博客来去深入对单例的理解。

一、什么是单例模式

单例模式 是一种常见的设计模式,核心思想是: 保证一个类在整个程序运行期间,只有唯一一个实例,并且提供一个全局访问点。它可以做到大大减少内存的使用,防止一个实例被重复创建从而占用内存空间,他在共享资源和对象的情况下非常有用。

下面给出一些 OC 中常见的单例

  • NSUserDefaults → 用户偏好设置
  • UIApplication → App 的入口对象
  • NSFileManager → 文件管理器
  • NSNotificationCenter → 通知中心

二、单例模式的优缺点

优点

  • 使整个应用内只有一个实例,避免数据冲突。
  • 提供了一个全局访问的点,使用方便,不需要每次都 alloc/init。
  • 减少了内存开销,节省资源。
  • 数据的一致性。

缺点

  • 因为是全局的状态,增加了耦合,相当于一个全局变量,如果被滥用,会导致模块之间的依赖性增强,代码的维护度降低。
  • 单例对象的生命周期和应用的周期一样长,很难被替换。
  • 在测试的时候很难被替换掉。
  • 使用单例的类,不显式传入依赖,而是全局取对象,导致代码逻辑不透明。
  • 而且他因为生命周期太长导致一直持有大量资源,内存占用过高。

三、模式介绍

1.懒汉模式(GCD & 互斥锁)

在这里我给出两个懒汉模式

要知道在写任何单例模式的时候都需要重写三个方法因为我们要让alloccopy还有mutablecopy这三个方法返回的单例保持一致,所以我们要重写如下几个方法。

+ (instancetype)allocWithZone:(struct _NSZone *)zone
- (id)copyWithZone:(NSZone *)zone
-(id)mutableCopyWithZone:(NSZone *)zone

GCD 写法

这个写法是苹果公司最推荐的写法,下面我会给出代码以及解释为什么要推荐这种写法。

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface LazyGcdSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@endNS_ASSUME_NONNULL_END
//懒汉式GCD写法
#import "LazyGcdSingleton.h"
static id _instance = nil;
@implementation LazyGcdSingleton
+ (instancetype)sharedInstance {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{_instance = [[super allocWithZone:NULL] init];});return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
- (id) mutableCopyWithZone:(NSZone *)zone {return [LazyGcdSingleton sharedInstance];
}
@end
  1. static dispatch_once_t onceToken;
  • 定义了一个静态变量 onceToken,它的作用就是 保证下面的 block 只会执行一次
  • dispatch_once_t 是苹果专门设计的类型,用来做单例的线程安全初始化。
  1. dispatch_once(&onceToken, ^{ … });
  • dispatch_once 内部帮你做了 加锁 + 判断是否执行过 的工作。
  • 无论多少线程同时调用 sharedInstance,block 里的代码只会被执行一次。
  • 所以这里的 _instance = [[super allocWithZone:NULL] init]; 只会执行一次,保证单例唯一性。
  1. _instance = [[super allocWithZone:NULL] init];
  • 真正创建对象的地方。
  • 用 super allocWithZone:NULL 是为了绕过 allocWithZone: 的重写(因为我们在类里也重写了它,防止外部直接用 alloc 创建新对象)。
  • 这样写就能保证只创建 一个实例对象

推荐这个写法的最根本的原因就是 dispatch_once 他会帮助你加锁和判断是否执行过的工作。

互斥锁写法(双重检查锁)

这里我给出代码以及会给出一些读者可能的疑问的解答

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface LazyLockSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSString *info;@endNS_ASSUME_NONNULL_END
#import "LazyLockSingleton.h"
static id _instance = nil;
@implementation LazyLockSingleton
+ (instancetype)sharedInstance {if (_instance == nil) {@synchronized (self) {if (_instance == nil) {_instance = [[super alloc] init];}}}return _instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {if (_instance == nil) {@synchronized (self) {if (_instance == nil) {_instance = [super allocWithZone:zone];}}}return _instance;
}
- (id) copyWithZone:(NSZone *)zone {return [LazyLockSingleton sharedInstance];
}
- (id) mutableCopyWithZone:(NSZone *)zone {return [LazyLockSingleton sharedInstance];
}
@end

这里可能会有这样几个问题会有疑惑

1.是否可以这样创建单例

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

实则不行,我们只是锁住了对象的创建,如果两个线程同时进入 if,那么就会产生两个对象。

2.为什么要用 static

如果不用 static,其他的类中可以使用 extern 来拿到这个单例

extern id instance;
instance = nil;

如果其他类中对单例进行如下操作,那么单例就会被重新创建,我们原本的单例对象中的内容就被销毁了。

2.饿汉模式

饿汉模式是一种单例模式实现方式,它在类加载的时候就创建好唯一的实例,保证整个程序运行期间全局只有一个对象。

它有几个非常奇妙的特点

  1. 线程安全
  • 因为实例在类加载阶段就已经创建完成,不存在多线程同时创建的问题。
  1. 立即初始化
  • 无论程序是否会用到这个对象,它都会在类加载时创建。
  1. 简单易实现
  • 只需一个静态变量初始化即可,不需要加锁或 dispatch_once。

缺点

  • 如果实例比较大或创建开销高,而程序又不一定会用到,可能浪费内存和启动时间。

下面给出代码

#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface EagerSingleton : NSObject<NSCopying, NSMutableCopying>
+ (instancetype) sharedInstance;
@property (nonatomic, strong) NSString *info;
@endNS_ASSUME_NONNULL_END
//饿汉式写法
#import "EagerSingleton.h"
static id _instance;
@implementation EagerSingleton
+ (void)load {_instance = [[self alloc] init];
}
+ (instancetype)sharedInstance {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

在这段代码中我个人认为

+ (void)load {_instance = [[self alloc] init];
}

这段代码完全体现了饿汉和懒汉的区别,它直接写了一个类方法来去创建了一个后面需要用到的对象。

总结

其实有一个很形象的说法来去阐述饿汉和懒汉的区别就是饿汉就是直接吃饱再去干活,而懒汉就是等到需要吃的时候再去吃和干活,

下面我来总结一下懒汉(GCD & 互斥锁)和饿汉

懒汉式 + 互斥锁(Mutex)

  • 核心逻辑:使用 @synchronized(self) 对实例创建加锁

  • 创建时机:第一次调用 sharedInstance 时才创建

  • 线程安全: 保证线程安全

  • 优点

    1. 按需创建(懒加载)
    2. 保证全局唯一
  • 缺点

    每次访问都加锁,性能较低

  • 使用场景:小型项目,理解单例原理

**懒汉式 + GCD (dispatch_once) **

  • 核心逻辑:使用 dispatch_once 保证 block 只执行一次
  • 创建时机:第一次调用 sharedInstance 时创建
  • 线程安全: 系统保证线程安全
  • 优点
    1. 按需创建(懒加载)
    2. 高效,只有第一次创建加锁
    3. 代码简洁、官方推荐
  • 缺点:几乎无明显缺点
  • 使用场景:iOS / macOS 官方标准单例实现

饿汉式

  • 核心逻辑:在类加载阶段就创建静态实例

  • 创建时机:类加载时(程序启动)

  • 线程安全: 天然线程安全

  • 优点

    1. 实现简单
    2. 天然线程安全
  • 缺点

    程序启动时就创建对象,如果对象大或未使用,可能浪费资源

  • 使用场景:必须全局存在的管理类,如配置管理器、日志管理器

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/96539.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96539.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/96539.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决Discord.py中的/help命令问题

在使用Discord.py开发机器人时,常常会遇到一些常见的问题,比如命令找不到或者命令功能不符合预期。本文将详细探讨如何解决在使用@bot.slash_command定义/help命令时遇到的问题,并提供一个完整的实例来展示如何正确设置这个命令。 问题描述 当你在Discord机器人中输入/hel…

解决VSCode默认F5配置无法启动调试器的问题

前几天做笔试&#xff0c;最后一题代码有点问题&#xff0c;习惯性地按了个F5启动gdb发现居然爆炸了&#xff0c;报错找不到编译出来的二进制文件&#xff0c;看着像是默认配置的问题&#xff0c;由于时间紧迫最后只能用输出大法解决。 感觉不可理喻&#xff0c;几年前调程序的…

webrtc弱网-LossBasedBweV2类源码分析与算法原理

1. 核心功能LossBasedBweV2是WebRTC Google Congestion Control (GoogCC) 算法套件中的第二代基于丢包的带宽估计器。它的核心功能是&#xff1a;带宽估计&#xff1a; 根据网络数据包的丢失情况&#xff0c;估算当前网络路径可用的带宽上限。其核心假设是&#xff1a;当发送速…

AI代理化检索:智能信息获取新范式

代理化检索(Agentic Retrieval)是一种由AI代理自主管理的信息检索范式,通过动态规划、工具调用和多步推理提升复杂查询的处理能力。其核心机制、技术实现和应用特点如下: 一、核心机制 自主决策循环 代理通过循环执行"规划-行动-观察"流程处理查询: 规划阶段:…

Android Studio中的各种Java版本区别

Android Studio中的各种Java版本 创建一个项目&#xff0c;app模块的build.gradle.kts默认配置如下&#xff1a; plugins {alias(libs.plugins.android.application)alias(libs.plugins.kotlin.android) }android {namespace "cn.android666.javaversiontest"comp…

ubuntu新增磁盘扩展LV卷

登录平台 login as: wqbboy wqbboy172.17.2.86s password: Welcome to Ubuntu 22.04.5 LTS (GNU/Linux 5.15.0-153-generic x86_64)* Documentation: https://help.ubuntu.com* Management: https://landscape.canonical.com* Support: https://ubuntu.com/proSyst…

Day 16: GAN生成对抗网络专项 - 从博弈论到艺术创作的完整之旅

Day 16: GAN生成对抗网络专项 - 从博弈论到艺术创作的完整之旅 🎯 学习目标: 深度掌握生成对抗网络理论与实践,从博弈论基础到风格迁移应用的完整技术栈 ⏰ 学习时长: 6小时深度学习 (理论3小时 + 实践3小时) 🔧 技术栈: PyTorch + 数学推导 + 经典架构 + 实战应用 💡 核…

《QT 108好类》之16 QComboBox类

《QT 108好类》之16 QComboBox类QT 108好类之16 QComboBox类QComboBox类特性和应用场景QComboBox类继承关系QComboBox类使用1 简单使用2 表单输入3 使用自定义模型和视图4 完全自定义弹出窗口QComboBox类类使用效果QT 108好类之16 QComboBox类 QComboBox是 常用的下拉框&#…

项目模块划分

项目模块划分 服务端模块&#xff1a; 持久化数据管理中心模块 在数据管理模块中管理交换机&#xff0c;队列&#xff0c;队列绑定&#xff0c;消息等部分数据数据。 \1. 交换机管理&#xff1a; a. 管理信息&#xff1a;名称&#xff0c;类型&#xff0c;是否持久化标志&#…

小白也能看懂!OpenCV 从零开始安装配置全教程(包含Windows / Ubuntu / 树莓派)系统详细操作配置教程

小白也能看懂&#xff01;OpenCV 从零开始安装配置全教程&#xff08;包含Windows / Ubuntu / 树莓派&#xff09;系统详细操作配置教程 摘要 本教程是面向“小白也能懂”的OpenCV安装与配置全攻略&#xff0c;涵盖Windows、Ubuntu和树莓派三大平台&#xff0c;真正实现“从零…

【华为云】容器镜像服务 SWR 详解:从上传下载到 ModelArts 应用

前言 华为云容器镜像服务&#xff08;Software Repository for Container&#xff0c;简称 SWR&#xff09;是华为云提供的企业级容器镜像仓库服务。它支持 Docker 镜像的存储、管理和分发&#xff0c;为容器化应用提供安全可靠的镜像托管服务。本文将详细介绍 SWR 的核心功能…

计算机网络知识点梳理(一)概述:组成、发展、性能、体系结构等

目录 一、互联网 &#xff08;1&#xff09;特点 &#xff08;2&#xff09;网络的组成 &#xff08;3&#xff09;网络、互连网、因特网 &#xff08;4&#xff09;互联网发展的三个阶段 &#xff08;5&#xff09;标准化 &#xff08;6&#xff09;组成 二、计算机网…

不同行业视角下的数据分析

声明&#xff1a;以下部分内容含AI生成 基于行业维度来划分数据分析岗位&#xff0c;可以帮助我们更好地理解不同行业对数据分析技能、业务知识和职业发展的独特要求。 目录 一、总体框架&#xff1a;为什么行业维度如此重要&#xff1f; 二、主要行业划分及详细讲解 1. 互联…

「CTF」青少年CTF·雏形系统

题目&#xff1a; 解题过程 尝试随便输入点什么&#xff0c;没有结果 使用dirsearch扫描网址目录 可以看到有扫描到一个www.zip&#xff0c;zip文件大概率有需要的东西 网址后加上www.zip就能对该文件进行下载 文件解压缩后如下 打开qsnctf.php&#xff0c;代码内容如下 <…

Java实战项目演示代码及流的使用

project 准备牌->洗牌->发牌 import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.TreeSet;public class PokerGameplus {static HashMap<Integer,String> hs new HashMap<>();static ArrayList<Int…

使用 OpenLayers + 高德瓦片源实现旅游足迹地图

作为一个热爱旅行的开发者&#xff0c;我一直想要一个能够记录和展示自己旅游足迹的功能。市面上虽然有很多地图应用&#xff0c;但大多功能复杂&#xff0c;而我只需要一个简单直观的方式来标记去过的地方和想去的地方。 于是我决定在自己的个人网站上实现一个旅游足迹地图功…

Redis基础(含常用命令等以快速入门)

一、初步认识 1、NoSQL SQL 关系型数据库&#xff08;表结构&#xff0c;强一致&#xff09;NoSQL 非关系型数据库&#xff08;灵活结构&#xff0c;最终一致&#xff0c;水平扩展爽&#xff09; 维度SQL&#xff08;关系型&#xff09;NoSQL&#xff08;非关系型&#xf…

OSPF特殊区域、路由汇总及其他特性

OSPF路由器需要同时维护域内路由、域间路由、外部路由信息数据库。当网络规模不断扩大时&#xff0c;LSDB规模也不断增长。如果某区域不需要为其他区域提供流量中转服务&#xff0c;那么该区域内的路由器就没有必要维护本区域外的链路状态数据库。OSPF通过划分区域可以减少网络…

在缓存Cacheable注解中Key值如何使用常量

1.在常量类中定义商品缓存空间和商品缓存KEY public interface CacheConstants {/*** Goods Cache Name*/String QNA_GOODS_CACHE "qna-goods";/*** Goods Cache key*/String QNA_GOODS_CACHE_KEY "qna_goods:";/*** Order Cache Name*/String QNA_ORDER…

sklearn聚类

在此将sklearn官网的一张关于聚类算法比较的图片放过来。 下面的表格是根据sklearn官网翻译而来。 方法名称 参数 可扩展性 应用场景 几何度量(距离) MiniBatchKMeans 簇的数量 非常适合处理大量样本和中等数量的簇(使用MiniBatch时) 通用型,适用于簇大小均匀、几何形状平…