当前位置: 首页 > news >正文

【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】单例模式 什么是单例模式&#xff1f; 定义 单例模式&#xff0c;简单地说就是一个类只对应一个对象&#xff0c;每次使用这个类时&#xff0c;都只能获取到那一个对象。它的详细定义如下&#xff1a; 如果一个类始终只能创建一个实例&#xff0c;则这个类被称为单例…...

Linux | 探索 Linux 信号机制:信号的产生和自定义捕捉

信号是 Linux 操作系统中非常重要的进程控制机制&#xff0c;用来异步通知进程发生某种事件。理解信号的产生、阻塞、递达、捕捉等概念&#xff0c;可以帮助开发者更好地编写健壮的应用程序&#xff0c;避免由于未处理的信号导致程序异常退出。本文将带你从基础概念开始&#x…...

递归的时间复杂度分析

确定回溯算法的时间复杂度通常比较复杂&#xff0c;因为它取决于搜索空间的大小以及你的剪枝效率。对于生成从1到n的所有长度为k的组合。分析这类算法的时间复杂度时&#xff0c;我们通常需要考虑递归树的所有可能路径。 组合数 生成的组合数量是从n个元素中选择k个的组合数&…...

C++: 二叉树进阶面试题

做每件事之前都心存诚意, 就会事半功倍. 目录 前言1. 根据二叉树创建字符串2. 二叉树的层序遍历Ⅰ3. 二叉树的层序遍历Ⅱ4. 二叉树的最近公共祖先5. 二叉搜索树与双向链表6. 根据一棵树的前序遍历与中序遍历构造二叉树7. 根据一棵树的中序遍历与后序遍历构造二叉树8. 二叉树的…...

【HarmonyOS NEXT】实现网络图片保存到手机相册

【问题描述】 给定一个网络图片的地址&#xff0c;实现将图片保存到手机相册 【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功能支持两种形式数据集读取自定义采样策略自动组装成批数据多进程数据加载自动实现锁页内存&#xff08;Pinning Memo…...

浅谈openresty

熟悉了nginx后再来看openresty&#xff0c;不得不说openresty是比较优秀的。 对nginx和openresty的历史等在这此就不介绍了。 首先对标nginx&#xff0c;自然有优劣 一、开发难度 nginx&#xff1a; 毫无疑问nginx的开发难度比较高&#xff0c;需要扎实的c/c基础&#xff…...

【学习笔记】2024最新版SpringCloud教程

2024最新版SpringCloud教程 0 前言闲聊开篇简介 1 SpringBoot和SpringCloud版本选型 2 SpringCloud是什么能干吗 3 SpringCloud各组件的停更升级替换说明 4 项目实战之需求说明 5 项目实战之Maven父工程聚合说明和mysql驱动选择 6 项目实战之Mapper4一键生成Dao层代码 …...

Proxyless Service Mesh:下一代微服务架构体系

一、项目背景及意义 在当今的微服务架构中&#xff0c;应用程序通常被拆分成多个独立的服务&#xff0c;这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性&#xff0c;但也带来了新的挑战&#xff0c;比如&#xff1a; 服务间通信的复杂性&#…...

大数据Flink(一百一十八):SQL水印操作(Watermark)

文章目录 ​​​​​​SQL水印操作&#xff08;Watermark&#xff09; 一、为什么要有WaterMark 二、​​​​​​​Watermark解决的问题 三、​​​​​​​​​​​​​​代码演示 ​​​​​​SQL水印操作&#xff08;Watermark&#xff09; 一、​​​​​​​为什么要…...

【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动

把QGroundControl地面站添加到Ubuntu侧边菜单栏启动 简介准备工作步骤 1: 创建 Desktop Entry 文件步骤 2: 编辑 Desktop Entry 文件步骤 3: 刷新应用程序菜单步骤 4: 将 QGroundControl 固定到侧边栏 环境&#xff1a; Ubuntu &#xff1a;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的鲜花商城系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的线…...

李飞飞任CEO,空间智能公司World Labs亮相,全明星阵容曝光

人工智能的下个大方向已经出现&#xff0c;标志性学者决定下场创业。 本周五&#xff0c;一个重磅消息引爆了 AI 圈&#xff1a;斯坦福大学计算机科学家李飞飞正式宣布创办 AI 初创公司 ——World Labs&#xff0c;旨在向人工智能系统传授有关物理现实的深入知识。 李飞飞说道&…...

PyTorch详解-可视化模块

PyTorch详解-可视化模块 Tensorboard 基础与使用启动 TensorBoard访问 TensorBoard使用 TensorBoardSummaryWriter类介绍参数说明常用方法 CNN卷积核与特征图可视化参数说明返回值 混淆矩阵与训练曲线可视化混淆矩阵可视化训练曲线绘制 模型参数打印参数说明输出解释 Tensorboa…...

Bootstrap 警告信息(Alerts)使用介绍

本章将讲解警告&#xff08;Alerts&#xff09;以及 Bootstrap 所提供的用于警告的 class。警告&#xff08;Alerts&#xff09;向用户提供了一种定义消息样式的方式。它们为典型的用户操作提供了上下文信息反馈。 您可以为警告框添加一个可选的关闭按钮。为了创建一个内联的可…...

uniapp(H5)设置反向代理,设置成功后页面报错

设置反向代理后&#xff0c;页面报错图&#xff1a; 反向代理代码&#xff1a;devServer下面就是配置对应的代理&#xff0c;一般这样就没问题了 "h5": {"router": {"mode": "hash"},"devServer": {"port": 517…...

define、typedef和using的使用

define、typedef 和 using 是 C&#xff08;以及 C 语言中的 define&#xff09;中用于定义别名或简化复杂类型的三个关键字&#xff0c;但它们各自有着不同的用途和行为。下面将分别对比这三个关键字&#xff1a; 1. #define 定义方式&#xff1a;#define 是预处理指令&…...

vue element时间选择不能超过今天 时间选中长度不能超过7天

背景&#xff1a; 使用elenmet plus 组件实现时间选择&#xff1b;且日期时间选择不能超过今天&#xff1b;连续选中时间的长度范围不能超过7天 效果展示&#xff1a; 实现思路&#xff1a; 一、使用element组件自带的属性和方法&#xff1b; :disabled-date"disabledDate…...

如何 吧一个 一维数组 切分成相同等分,一维数组作为lstm的输入(三维数据)的数据预处理 collate_fn的应用

要将一个一维数组切分成相同等分&#xff0c;你可以使用 Python 的内置功能或者 NumPy 库&#xff08;如果你处理的是数值数据&#xff09;。以下是几种不同的方法&#xff1a; 方法3 pad_sequence 结合dataloader 应该是最佳方案 ### 方法 1: 使用 Python 的内置切片功能 如果…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

Leetcode 3577. Count the Number of Computer Unlocking Permutations

Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接&#xff1a;3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯&#xff0c;要想要能够将所有的电脑解锁&#x…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

Linux系统部署KES

1、安装准备 1.版本说明V008R006C009B0014 V008&#xff1a;是version产品的大版本。 R006&#xff1a;是release产品特性版本。 C009&#xff1a;是通用版 B0014&#xff1a;是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存&#xff1a;1GB 以上 硬盘&#xf…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...

车载诊断架构 --- ZEVonUDS(J1979-3)简介第一篇

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 做到欲望极简,了解自己的真实欲望,不受外在潮流的影响,不盲从,不跟风。把自己的精力全部用在自己。一是去掉多余,凡事找规律,基础是诚信;二是…...

高分辨率图像合成归一化流扩展

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 1 摘要 我们提出了STARFlow&#xff0c;一种基于归一化流的可扩展生成模型&#xff0c;它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流&#xff08;TARFlow&am…...