【iOS】单例模式
【iOS】单例模式
什么是单例模式?
定义
单例模式,简单地说就是一个类只对应一个对象,每次使用这个类时,都只能获取到那一个对象。它的详细定义如下:
如果一个类始终只能创建一个实例,则这个类被称为单例类。 有一个全局的接口来访问这个实例。当第一次载入的时候,它通常使用延时加载的方法创建单一实例。 在程序中,一个单例类在程序中只能初始化一次,为了保证在使用中始终都是存在的,所以单例是在存储器的全局区域,在编译时分配内存,只要程序还在运行就会一直占用内存,在APP结束后由系统释放这部分内存 系统方法中,有未知的自动释放池,如果一个对象进行自动释放的话,又可能进入未知的自动释放池,出现内存问题。这就是单例模式不能自动释放的原因
特点
1.单例是一个类,创造出来的对象叫单例对象
2.单例对象使用类方法创建
3.单例一旦被创建,程序结束才会释放,也就是说,单例只初始化一次
4.单例不用程序员管理内存,内存随程序关闭而释放
为什么使用单例模式?
有时候我们需要一个全局的对象,而且要保证全局有且只有一份即可,这时候就需要用到单例设计模式,需要注意:在多线程的环境下做好线程保护。 一般在程序中,经常调用的类,如工具类、公共跳转类等都会采用单例模式
常见的单例模式
在OC语言中,有一些原生的单例类
| UIApplication | 应用程序实例类 |
|---|---|
| NSNotificationCenter | 消息中心类 |
| NSFileManager | 文件管理类 |
| NSUserDefaults | 应用程序设置 |
| NSURLCache | 请求缓存类 |
| NSHTTPCookieStorage | 应用程序cookies池 |
如何使用单例模式?
static关键字
在了解如何使用单例模式之前,我们先来了解一个与之相关的属性修饰关键字——static
static关键字用于修饰局部变量、全局变量和函数。修饰局部变量时,表示该局部变量存储在静态区,修饰全局变量时,表示限制该全局变量只能在当前文件中访问,修饰函数时,则表示限制函数只能在当前源文件中使用。
静态内存存储区在整个程序运行期间都存在
我们需要用static关键字来修饰单例变量,将变量限制在当前类的文件,否则在其他类中可以使用extern来拿到这个单例并修改
创建单例
我们来试着创建一个单例
#import <Foundation/Foundation.h>NS_ASSUME_NONNULL_BEGIN@interface singleton : NSObject
+ (id)instance;
@endNS_ASSUME_NONNULL_END#import "singleton.h"@implementation singleton
static id instance = nil;+ (id)instance {if (!instance) {instance = [[super allocWithZone:NULL] init];}return instance;
}@end#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "singleton.h"int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {NSLog(@"singleton:%d",[singleton instance] == [singleton instance]);NSLog(@"%d",[singleton instance] == [[singleton alloc] init]);}return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
运行后得到以下结果:

我们发现,当使用自定义的类方法创建对象时,的确实现了我们想要的效果,创建出了单例。但是,当使用alloc init创建对象时,得到的并不是单例,与使用类方法创建的对象也并不相同。
这个时候我们就需要重写alloc init方法,来保证无论使用什么方法创建,这个类始终只有一个对象。这时我们只需重写allocWithZone:方法就可以了。
我们平时在初始化对象时使用的方法[[Class alloc] init],其实做了两件事,alloc方法为对象分配内存空间,init是对对象的初始化(包括设置成员变量初值等工作)。而给对象分配内存空间,除了alloc方法,还有一个方法就是allocWithZone:。使用alloc方法时,其实也是调用了allocWithZone方法的,所以只需要在allocWithZone里重写,就可以覆盖所有会生成新的实例的方法。
这里的allocWithZone:方法,它的参数是被忽略的,正确的做法是传nil或者NULL。
此外,为了防止copy方法生成新的实例,还要重写copyWithZone和mutableCopyWithZone。
#import "singleton.h"@implementation singleton
static id instance = nil;+ (id)instance {if (!instance) {instance = [[super allocWithZone:NULL] init];}return instance;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone{return instance;
}
-(id)copyWithZone:(NSZone *)zone{return instance;
}-(id)mutableCopyWithZone:(NSZone *)zone{return instance;
}@end
懒汉模式
创建单例有两种模式,分别是懒汉模式和饿汉模式。这两种模式的区别就在于在什么时候去创建对象,懒汉模式就是在第一次用到类的时候才会实例化,饿汉模式就是在类加载的时候就进行实例化。下面先介绍一下懒汉模式
懒汉模式一般用于访问量较小的时候,用时间换空间。通俗地说,懒汉模式就像一个大懒虫,这个懒虫现在有很多穿过的脏衣服,但是他却不打算洗这些衣服,等到实在没衣服穿了,他才会去洗这些衣服。
在使用懒汉模式的时候,要注意保证线程的安全性。
多线程和单线程
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,也就是说不同的线程可以执行同样的函数。
多线程是指程序中包含多个执行流,也就是在一个程序中可以同时运行多个不同的线程来执行不同的任务
加锁
在单线程中,我们上面所用到的创建单例的方法其实就是懒汉模式,可以直接使用上面的方法来创建懒汉模式,而在多线程中,为了确保线程安全,我们可以用@synchronized来加锁,@synchronized可以防止不同的线程同时执行同一段代码,因此加锁后每次访问该代码就都只能一个行程进行访问。
#import "FirstMethod.h"@implementation FirstMethod
static id instance = nil;
+ (instancetype)instance {@synchronized (self) {if (!instance) {instance = [[super allocWithZone:NULL] init];}}return instance;
}
@end
这一段代码可以在确保线程安全的情况下实现单例,但是我们细看会发现调用insrance方法后,如果instance已经存在,可以直接将instance作为返回值传递出去,没有必要进入锁。于是我们加锁之前先做一次判断
#import "FirstMethod.h"@implementation FirstMethod
static id instance = nil;
+ (instancetype)instance {if (!instance) {@synchronized (self) {if (!instance) {instance = [[super allocWithZone:NULL] init];}}}return instance;
}
@end
注意:这里内层的if不能省略,因为如果省略了内层的if,只是对对象的创建和初始化加了锁,而不是对创建单例过程加锁,也就是说有可能会有两个线程同时进入外层的if,然后先后一个一个执行alloc init,这样就可能导致创建出两个不一样的对象。
GCD的dispatch_once
GCD:全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”,是苹果公司为多核的并行运算提出的解决方案,会自动利用更多的CPU内核(比如双核、四核),自动管理线程的生命周期(创建线程、调度任务、销毁线程),程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码。
另一种确保线程安全的方法就是使用GCD的dispatch_once来创建单例
#import "singleton.h"@implementation singleton
static id instance = nil;+ (id)instance {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance = [[super alloc] init];});return instance;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone{return instance;
}
@end
下面解释一下dispatch_once是怎样确保线程安全并创建单例的。
我们看到上述代码中出现了dispatch_once_t变量和dispatch_once函数
dispatch_once_t变量

这里的intptr_t长度是所在平台的位数,用来存放地址。
dispatch_once函数

dispatch_once无论单线程还是多线程,都只执行一次,在安全的前提下也保证了性能,同时实现了单例的创建。
当onceToken为0时,线程执行dispatch_once的block中的代码
当onceToken为-1时,线程跳过dispatch_once的block中的代码
当onceToken为其他值时,线程被阻塞,等待onceToken值改变
饿汉模式
#import "singleton.h"@implementation singleton
static id instance = nil;+ (void)load {[super load];instance = [[self allocWithZone:NULL] init];
}+ (id)instance {instance = [[self allocWithZone:NULL] init];return instance;
}
@end
load方法:当类加载到运行环境中时调用且仅调用一次,同时一个类只会加载一次(程序启动时,所有类加载一次,不管有没有在当前视图中用到)
相关文章:
【iOS】单例模式
【iOS】单例模式 什么是单例模式? 定义 单例模式,简单地说就是一个类只对应一个对象,每次使用这个类时,都只能获取到那一个对象。它的详细定义如下: 如果一个类始终只能创建一个实例,则这个类被称为单例…...
Linux | 探索 Linux 信号机制:信号的产生和自定义捕捉
信号是 Linux 操作系统中非常重要的进程控制机制,用来异步通知进程发生某种事件。理解信号的产生、阻塞、递达、捕捉等概念,可以帮助开发者更好地编写健壮的应用程序,避免由于未处理的信号导致程序异常退出。本文将带你从基础概念开始&#x…...
递归的时间复杂度分析
确定回溯算法的时间复杂度通常比较复杂,因为它取决于搜索空间的大小以及你的剪枝效率。对于生成从1到n的所有长度为k的组合。分析这类算法的时间复杂度时,我们通常需要考虑递归树的所有可能路径。 组合数 生成的组合数量是从n个元素中选择k个的组合数&…...
C++: 二叉树进阶面试题
做每件事之前都心存诚意, 就会事半功倍. 目录 前言1. 根据二叉树创建字符串2. 二叉树的层序遍历Ⅰ3. 二叉树的层序遍历Ⅱ4. 二叉树的最近公共祖先5. 二叉搜索树与双向链表6. 根据一棵树的前序遍历与中序遍历构造二叉树7. 根据一棵树的中序遍历与后序遍历构造二叉树8. 二叉树的…...
【HarmonyOS NEXT】实现网络图片保存到手机相册
【问题描述】 给定一个网络图片的地址,实现将图片保存到手机相册 【API】 phAccessHelper.showAssetsCreationDialog【官方文档】 https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-photoaccesshelper-V5#showassetscreationdialog…...
Pytorch详解-数据模块
Pytorch详解-数据模块 torch.utils.data.Dataset数据交互模块—Dataset的功能示例系列APIsconcatSubsetrandom_splitsampler unsqueeze DataLoaderDataLoader功能支持两种形式数据集读取自定义采样策略自动组装成批数据多进程数据加载自动实现锁页内存(Pinning Memo…...
浅谈openresty
熟悉了nginx后再来看openresty,不得不说openresty是比较优秀的。 对nginx和openresty的历史等在这此就不介绍了。 首先对标nginx,自然有优劣 一、开发难度 nginx: 毫无疑问nginx的开发难度比较高,需要扎实的c/c基础ÿ…...
【学习笔记】2024最新版SpringCloud教程
2024最新版SpringCloud教程 0 前言闲聊开篇简介 1 SpringBoot和SpringCloud版本选型 2 SpringCloud是什么能干吗 3 SpringCloud各组件的停更升级替换说明 4 项目实战之需求说明 5 项目实战之Maven父工程聚合说明和mysql驱动选择 6 项目实战之Mapper4一键生成Dao层代码 …...
Proxyless Service Mesh:下一代微服务架构体系
一、项目背景及意义 在当今的微服务架构中,应用程序通常被拆分成多个独立的服务,这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性,但也带来了新的挑战,比如: 服务间通信的复杂性&#…...
大数据Flink(一百一十八):SQL水印操作(Watermark)
文章目录 SQL水印操作(Watermark) 一、为什么要有WaterMark 二、Watermark解决的问题 三、代码演示 SQL水印操作(Watermark) 一、为什么要…...
【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动
把QGroundControl地面站添加到Ubuntu侧边菜单栏启动 简介准备工作步骤 1: 创建 Desktop Entry 文件步骤 2: 编辑 Desktop Entry 文件步骤 3: 刷新应用程序菜单步骤 4: 将 QGroundControl 固定到侧边栏 环境: Ubuntu :20.04 LTS 简介 QGroundControl 是…...
PostgreSQL配置主从同步
PostgreSQL配置主从同步 1 主、备库安装postgresql软件 su - pg12 cd /home/pg12/resource tar -zxvf postgresql-12.9.tar.gz cd postgresql-12.9/ ./configure --prefix/home/pg12/soft/ make -j 16 && make install2 主、备库配置环境变量 vi ~/.bash_profile…...
基于python+django+vue的鲜花商城系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…...
李飞飞任CEO,空间智能公司World Labs亮相,全明星阵容曝光
人工智能的下个大方向已经出现,标志性学者决定下场创业。 本周五,一个重磅消息引爆了 AI 圈:斯坦福大学计算机科学家李飞飞正式宣布创办 AI 初创公司 ——World Labs,旨在向人工智能系统传授有关物理现实的深入知识。 李飞飞说道&…...
PyTorch详解-可视化模块
PyTorch详解-可视化模块 Tensorboard 基础与使用启动 TensorBoard访问 TensorBoard使用 TensorBoardSummaryWriter类介绍参数说明常用方法 CNN卷积核与特征图可视化参数说明返回值 混淆矩阵与训练曲线可视化混淆矩阵可视化训练曲线绘制 模型参数打印参数说明输出解释 Tensorboa…...
Bootstrap 警告信息(Alerts)使用介绍
本章将讲解警告(Alerts)以及 Bootstrap 所提供的用于警告的 class。警告(Alerts)向用户提供了一种定义消息样式的方式。它们为典型的用户操作提供了上下文信息反馈。 您可以为警告框添加一个可选的关闭按钮。为了创建一个内联的可…...
uniapp(H5)设置反向代理,设置成功后页面报错
设置反向代理后,页面报错图: 反向代理代码:devServer下面就是配置对应的代理,一般这样就没问题了 "h5": {"router": {"mode": "hash"},"devServer": {"port": 517…...
define、typedef和using的使用
define、typedef 和 using 是 C(以及 C 语言中的 define)中用于定义别名或简化复杂类型的三个关键字,但它们各自有着不同的用途和行为。下面将分别对比这三个关键字: 1. #define 定义方式:#define 是预处理指令&…...
vue element时间选择不能超过今天 时间选中长度不能超过7天
背景: 使用elenmet plus 组件实现时间选择;且日期时间选择不能超过今天;连续选中时间的长度范围不能超过7天 效果展示: 实现思路: 一、使用element组件自带的属性和方法; :disabled-date"disabledDate…...
如何 吧一个 一维数组 切分成相同等分,一维数组作为lstm的输入(三维数据)的数据预处理 collate_fn的应用
要将一个一维数组切分成相同等分,你可以使用 Python 的内置功能或者 NumPy 库(如果你处理的是数值数据)。以下是几种不同的方法: 方法3 pad_sequence 结合dataloader 应该是最佳方案 ### 方法 1: 使用 Python 的内置切片功能 如果…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
Qemu arm操作系统开发环境
使用qemu虚拟arm硬件比较合适。 步骤如下: 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载,下载地址:https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...
适应性Java用于现代 API:REST、GraphQL 和事件驱动
在快速发展的软件开发领域,REST、GraphQL 和事件驱动架构等新的 API 标准对于构建可扩展、高效的系统至关重要。Java 在现代 API 方面以其在企业应用中的稳定性而闻名,不断适应这些现代范式的需求。随着不断发展的生态系统,Java 在现代 API 方…...
华为OD机试-最短木板长度-二分法(A卷,100分)
此题是一个最大化最小值的典型例题, 因为搜索范围是有界的,上界最大木板长度补充的全部木料长度,下界最小木板长度; 即left0,right10^6; 我们可以设置一个候选值x(mid),将木板的长度全部都补充到x,如果成功…...

