【iOS】 单例模式

1. 认识单例模式

请添加图片描述

首先让我们先看下关于单例模式的定义(来自于《设计模式》(Addison-Wesley,1994))

一个类有且仅有一个实例,并且自行实例化向整个系统提供。

如果说每一个人都是一个类,那么从他出生开始,他就是生活中的唯一实例,每当有人要拜访或者联系你的时候,无论别人认识你的时候你是什么状态,他所能联系到的都是现在的你。你本身的状态会在其他地方发生改变,即当你状态改变后,后续所有找到你的,都是看到状态改变后的你。那么,我们就可以认为每一个人都处于单例模式。

在 iOS 中,系统默认就有许多支持单例方法的地方,例如通知中心、应用管理、本地保存等,但是一般都属于非严格的单例模式,也就是你可以使用 [UIApplication sharedApplication] 来获取单例对象,也可以通过 [UIApplication new] 生成一个新的对象,而如果按照准确的单例定义来说,依旧以 UIApplication 类为例, [UIApplication new][UIApplication sharedApplication] 应该在任何时候返回的对象都是相同的。也就是说无论你怎么操作,只会存在一个实例,不会创建其他的副本内容。

2. 单例模式的使用

单例模式需要实现一个公共访问的类方法,一般命名为 shared + 类名。在该方法的具体实现方案,是推荐通过dispatch_once 来实现类的实例化。

可以直接通过重写父类的方法,把分配内存的方法变成只执行一次。从根本上实现了单例。

如果按照严格的单例写法的话,单例模式一共需要重写五个方法 :

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

或者,可以把这些方法全都通过attribute((unavailable (invalid ))); 方式禁止,以保证一定使用shhared的单例方法。

SingleView.h#import <Foundation/Foundation.h>@interface SingleView : NSObject#pragma mark- method
+ (SingleView *)sharedSingleView;
+ (id)alloc __attribute__((unavailable("invalid, use sharedSingleView instead")));
+ (id)new __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)copy __attribute__((unavailable("invalid, use sharedSingleView instead")));
- (id)mutableCopy __attribute__((unavailable("invalid, use sharedSingleView instead")));@end

截屏2022-07-31 12.03.41.png

如果只是在自己的项目中使用的话,那么直接实现一个 shared 的类方法基本都能满足需求,由于是自己的内容,代码是受控的,实现并调用即可。而如果是对外的库的话(静态库、动态库),这需要根据具体的业务内容考虑是否需要按照严格的单例写法来实现了。

懒汉

懒汉式创建单例模式的意思其实就是指我们在创建的时间是我们需要用到这个单例的时候,我们才开始创建这个唯一实例,这种创建模式有助于提高性能,以及节省资源的效果。简单来说,就是我们平时日常生活中的deadline,只要在达到deadline的时候我们才去提交工作,这和他的名字也很相似,懒汉式延迟创建。

dispatch_once

//SingleView.h#import <Foundation/Foundation.h>@interface SingleView : NSObject
+ (SingleView *)sharedSingleView;
@end
//SingleView.m#import "SingleView.h"@implementation SingleView#pragma mark- public method
+ (SingleView *)sharedSingleView {static SingleView *single = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{single = [[super allocWithZone:NULL] init];});return single;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}+ (id)copyWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}+ (id)mutableCopyWithZone:(struct _NSZone *)zone {return [SingleView shareSingleView];
}- (id)copyWithZone:(NSZone *)zone{return  [SingleView shareSingleView];
}- (id)mutableCopyWithZone:(NSZone *)zone{return [SingleView shareSingleView];
}
@end

同步锁实现

static Singleton* instance = nil;
+(id) sharedInstance  {if (instance == nil) {@synchronized (self) {if (instance == nil) {instance = [[super allocWithZone:NULL] init];}}}return instance;
}+(id) allocWithZone:(struct _NSZone *)zone {if (instance == nil) {@synchronized (self) {if (!instance) {instance = [[super allocWithZone:NULL] init];}}}return instance;
}-(id) copyWithZone:(NSZone *)zone {return self;
}-(id) mutableCopyWithZone:(NSZone *)zone {return  self;
}
@end

饿汉

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

static id instance = nil;+ (void)load {instance = [[super allocWithZone:NULL] init];
}+ (instancetype)sharedInstance {return instance;
}+ (instancetype)allocWithZone:(struct _NSZone *)zone {return instance;
}- (instancetype)init {static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{NSLog(@"init一次");});return self;
}- (id)copyWithZone:(NSZone *)zone {return self;
}- (id)mutableCopyWithZone:(NSZone *)zone {return self;
}

输出结果如下证明单例代码有效:
请添加图片描述

因为对block部分对不了解,引用学长博客中的一段话:

ispatch_once 主要是根据 onceToken 的值来决定怎么去执行代码。
1.当 onceToken = 0 时,线程执行 dispatch_once 的 block 中代码;
2.当 onceToken = -1 时,线程跳过 dispatch_once 的 block 中代码不执行;
3.当 onceToken 为其他值时,线程被阻塞,等待 onceToken 值改变。
当线程调用mySingleton方法时,此时 onceToken = 0,调用 block 中的代码,此时 onceToken =其他值。
当其他线程再调用 mySingleton 方法时,onceToken为其他值,线程阻塞。当 block 线程执行完 block之后,onceToken = -1,其他线程不再阻塞,跳过 block。下次再调用mySingleton方法 时, block 已经为-1,直接跳过 block。转载自[【iOS】—— 单例模式]
(https://blog.csdn.net/m0_73974920/article/details/132909916?spm=1001.2014.3001.5502)
这里又要注意一下要调用父类的【super allocWithZone:null】这个方法,因为我们这里已经重写这个类的方法了,不然会出现一个循环调用的问题。同时为了防止copy和mutablecopy两个方法的出现崩溃的问题。

3.总结

单例模式是一种非常常见、使用频率也很高的一种设计模式。单例能够在许多场合使用,有时候也可以用来保存数据。
它(严格单例)存在着以下特点:

节省内存;
在程序的运行周期中保存数据;
只能在自己的类中实现实例;
程序的运行周期中,内存不会被释放;
类中有且仅有一个实例;

懒汉模式:

· 优点:
延迟加载:懒汉模式只有在第一次访问单例实例时才会进行初始化,可以节省资源,提高性能,因为实例只有在需要时才会被创建。
节省内存:如果单例对象很大或者初始化过程开销较大,懒汉模式可以避免在程序启动时就创建不必要的对象。
线程安全性:可以通过加锁机制(如双重检查锁定)来实现线程安全。

· 缺点:
线程安全性开销:懒汉模式在实现线程安全时可能需要额外的同步机制,这会引入一些性能开销。
复杂性增加:实现线程安全的懒汉模式可能需要编写复杂的代码,容易引入错误。
缺点:
线程安全性开销:懒汉模式在实现线程安全时可能需要额外的同步机制,这会引入一些性能开销。
复杂性增加:实现线程安全的懒汉模式可能需要编写复杂的代码,容易引入错误。

饿汉模式:

优点:
简单:饿汉模式实现简单,不需要考虑线程安全问题,因为实例在类加载时就已经创建。
线程安全性:由于实例在类加载时创建,不会存在多个实例的风险,因此线程安全。

缺点:
无法实现延迟加载:饿汉模式在程序启动时就创建实例,无法实现延迟加载,可能会浪费资源,特别是当实例很大或初始化开销较大时。
可能引起性能问题:如果单例类的实例在程序启动时没有被使用,那么创建实例的开销可能是不必要的。
不适用于某些情况:如果单例对象的创建依赖于某些外部因素,而这些因素在程序启动时无法确定,那么饿汉模式可能不适用。

总的来说,懒汉模式适用于需要延迟加载实例的情况,可以节省资源和提高性能,但需要考虑线程安全性。饿汉模式适用于需要简单实现和线程安全性的情况,但不支持延迟加载。选择哪种模式应根据具体需求和性能考虑来决定。

请添加图片描述

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

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

相关文章

多目标轮廓匹配

前面我们使用模板匹配&#xff0c;得到的结果都是一个图&#xff0c;那么如果我们图片中有许多我们的目标&#xff0c;那么该如何找出来呢&#xff1f;如上我们图片中有许多箭头和我们的模板一致&#xff0c;只不过方向不对&#xff0c;那么该如何匹配呢&#xff1f;图片和模板…

【C++】简单介绍lambda表达式

各位大佬好&#xff0c;我是落羽&#xff01;一个坚持不断学习进步的学生。 如果您觉得我的文章还不错&#xff0c;欢迎多多互三分享交流&#xff0c;一起学习进步&#xff01; 也欢迎关注我的blog主页: 落羽的落羽 文章目录一、 什么是lambda表达式二、 表达式语法三、lambd…

磁共振成像原理(理论)4:自由进动和弛豫 (Free Precession and Relaxation)

当磁化自旋系统被射频脉冲扰动而偏离其热平衡态后&#xff0c;一旦移除外部激励并给予足够时间&#xff0c;系统将根据热力学定律返回平衡态。这一过程包含三个特征现象&#xff1a; (a) 自由进动——宏观磁化矢量 (M⃗\vec{M}M) 绕( B0⃗\vec {B_0}B0​​ )场的进动&#xff1…

ubuntu 20.04 安装spark

安装openjdk21 下载 wget https://download.java.net/openjdk/jdk21/ri/openjdk-2135_linux-x64_bin.tar.gz解压 tar -xvf openjdk-2135_linux-x64_bin.tar.gzsudo mv jdk-21/ /opt/jdk-21/设置环境变量 echo export JAVA_HOME/opt/jdk-21 | sudo tee /etc/profile.d/java2…

第三方区块链应用测评:【多签钱包合约安全评估_阈值签名机制与私钥存储安全性测试】

阈值签名机制安全测试密码学审计 采用门限签名方案&#xff08;TSS&#xff09;的多签钱包需验证其阈值BLS签名或ECDSA签名算法的正确性。测试重点包括&#xff1a;分布式密钥生成&#xff08;DKG&#xff09;过程的保密性&#xff08;无密钥信息泄露&#xff09;、签名碎片验证…

大模型处理长文档的挑战和解决方案?

当前&#xff0c;AI 应用正处于极速发展阶段&#xff0c;大语言模型&#xff08;LLM&#xff09;与检索增强生成&#xff08;RAG&#xff09;系统已成为构建智能问答、知识管理等高阶 AI 应用的核心引擎&#xff0c;被广泛应用于金融分析、学术研究、企业合规等多个领域。然而&…

JavaWeb--day1--HTMLCSS

(以下内容全部来自上述课程及课件) web开发介绍 1. 什么是web&#xff1f; Web&#xff1a;全球广域网&#xff0c;也称为万维网&#xff0c;能够通过浏览器访问的网站。 2. Web网站的工作流程 3. Web标准 Web标准也称为网页标准&#xff0c;由一系列的标准组成&#xf…

OpenEuler安装gitlab,部署gitlab-runner

目录 一、安装gitlab 二、安装部署docker设置源 三、下载部署runner ​编辑 四、构建CI/CD 一、安装gitlab 1.查看OpenEuler版本 [rootlocalhost ~]# cat /etc/os-release NAME"openEuler" VERSION"24.03 (LTS-SP1)" ID"openEuler" VERSI…

实战项目-----在图片 hua.png 中,用红色画出花的外部轮廓,用绿色画出其简化轮廓(ε=周长×0.005),并在同一窗口显示

实战项目实现以下功能&#xff1a;对图片 hua.png 进行轮廓提取&#xff0c;并在同一窗口中完成以下两个绘制操作&#xff1a;用红色画出花的外部轮廓&#xff08;即最外层轮廓&#xff09;用绿色画出该轮廓的近似多边形&#xff0c;其中近似精度参数 ε 设置为轮廓周长的 0.00…

开源鸿蒙北向框架开发:系统服务理论详解

系统服务的启动 基本可以认为&#xff1a;OpenHarmony 的系统服务进程都是“由 init 直接或间接拉起”的。 直接方式&#xff1a; init 按 /system/etc/init/.cfg 启动可执行&#xff08;如 /system/bin/sa_main、/system/bin/samgr 等&#xff09;&#xff0c;这些进程的 PPid…

龙虎榜——20250909

上证指数今天缩量收阴线&#xff0c;跌破10日均线&#xff0c;目前日线总体处于高位宽幅震荡中&#xff0c;小级别暂未明确方向&#xff0c;指数面临方向选择&#xff0c;需要注意高位股的风险。 深证指数今天缩量收阴线&#xff0c;跌破5日均线&#xff0c;接下来几天方向的选…

基于dijkstra算法的WSN网络MAC协议matlab仿真,分析网络延迟与网络开销

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.部分程序 4.算法理论概述 5.完整程序 1.程序功能描述 无线传感器网络&#xff08;WSN, Wireless Sensor Network&#xff09;是由大量低成本、低功耗的传感器节点组成&#xff0c;通过无线通信实现数据采集、传输与…

ES数据库启动时可以设置1G一下内存吗

可以&#xff0c;但强烈不建议在生产环境中这样做。ES 7.0 版本确实可以设置小于 1GB 的堆内存&#xff0c;但这会带来严重的性能问题和稳定性风险。 快速回答 # 最小化配置示例 - 仅用于测试或开发环境 export ES_JAVA_OPTS"-Xms512m -Xmx512m" ./bin/elasticsearch…

TI-92 Plus计算器:单位换算功能介绍

1 TI-92 Plus计算器&#xff1a;单位换算功能介绍 TI-92 Plus 内置了全面的单位换算功能&#xff0c;支持长度、质量、时间、温度、面积、体积、速度等数十种物理量的单位转换&#xff0c;操作直观&#xff0c;无需手动输入换算系数。以下是具体使用方法、示例及功能特点&#…

雪球科技Java开发工程师笔试题

单选 1.下列哪些语句关于内存回收的说明是正确的?( C ) A.内存回收程序允许程序员直接释放内存 B.程序员必须创建一个线程来释放内存 C.内存回收程序负责释放无用内存 D.内存回收程序可以在指定的时间释放内存对象 2.以下哪项不是Java基础类型(A) A.String B.int C.b…

NV3041A-01芯片屏幕

1. 核心概览这是一款集成了电源管理、显示内存&#xff08;RAM&#xff09;、时序控制等多种功能的单片显示驱动芯片&#xff08;通常称为Driver IC&#xff09;。它采用COG&#xff08;Chip-On-Glass&#xff09; 工艺&#xff0c;直接将芯片绑定在玻璃基板上&#xff0c;使得…

aiagent知识点

一、MCP (Model Context Protocol) 1. 核心概念是什么&#xff1a;MCP是一个开放协议&#xff0c;用于在应用&#xff08;如IDE、Agent&#xff09; 和工具/数据源&#xff08;如服务器、数据库&#xff09; 之间建立标准化的通信。目标&#xff1a;解决AI工具生态的碎片化问题…

第2节-过滤表中的行-WHERE

摘要&#xff1a;在本教程中&#xff0c;您将学习如何使用 PostgreSQL 的 WHERE 子句来筛选表中的行。 PostgreSQL WHERE 子句 SELECT FROM 语句从表中所有行的一个或多个列中查询数据。实际上&#xff0c;你经常需要选择满足某个条件的行。 要根据条件从表中筛选行&#xf…

IACheck赋能AI环评报告审核,推动环保设备制造行业发展

在“双碳目标”和绿色制造的背景下&#xff0c;环保设备制造行业正在迎来快速发展。然而&#xff0c;环评报告作为项目合规的“通行证”&#xff0c;却一直是企业最头疼的环节之一&#xff1a;编写复杂、审核周期长、错误率高。传统的审核模式不仅耗时耗力&#xff0c;还容易出…

常见的多态

一、核心概念多态&#xff08;Polymorphism&#xff09; 的字面意思是“多种形态”。在Java中&#xff0c;它指的是&#xff1a;同一个行为&#xff08;方法&#xff09;具有多个不同表现形式或形态的能力。更具体地说&#xff0c;它允许你&#xff1a;父类的引用指向子类的对象…