「iOS」——单例模式

iOS学习

  • 前言
  • 单例模式的概念
  • 单例模式的优缺点
  • 单例模式的两种模式
    • 懒汉模式
    • 饿汉模式
    • 单例模式的写法
  • 总结

前言

在一开始学习OC的时候,我们初步接触过单例模式。在学习定时器与视图移动的控件中,我们初步意识到单例模式的重要性。对于我们需要保持的控件,使用单例可以有效的防止再次创建而造成的bug,现在我们深入学习一下单例模式。


单例模式的概念

如果一个类有且仅有一个实例,并提供一个类方法供全局调用,那么这个类就被称为单例类。
而使用这样的类,就是单例模式。

单例模式的优缺点

优点:

  • 全局访问:单例模式确保一个类只有一个实例,并提供全局访问点,方便在整个应用中共享数据或功能。
  • 节省资源:由于只创建一个实例,可以减少内存开销,避免重复创建相同对象。
  • 控制实例化:通过私有构造函数,防止外部代码创建多个实例,确保数据一致性。

缺点:

  • 隐藏依赖:使用单例可能导致代码中隐藏的依赖关系,增加了代码的耦合性,降低了可测试性。
  • 难以扩展:单例模式不易于扩展,若需要改变实例的行为,可能需要修改单例类的代码。
  • 线程安全问题:在多线程环境下,单例的实现需要特别注意线程安全,若处理不当可能导致数据不一致。

单例模式的两种模式

实现单例模式,我们需要保证永远只创建一个实例。那么创建实例的方法有四种,因此我们需要分别改写这四种方法。

  • 通过alloc init创建
  • 通过类方法创建
  • 通过copy创建
  • 通过mutableCopy 创建

懒汉模式

懒汉模式的主要特点是在需要时才会创建单例对象的实例,而不是在程序启动时就立即创建。通过延迟对象的初始化来节省资源和提高性能。
如同名字,除了在用到时“懒得创建”。


#import "Singletion.h"@implementation Singletion
static id instance = nil;+(instancetype)allocWithZone:(struct _NSZone *)zone
{if(instance == nil) {@synchronized (self) {if(instance == nil) {instance = [super allocWithZone:zone];}}}return instance;
}+(instancetype)mySingletion {if (instance == nil) {@synchronized (self) {instance = [[self alloc] init];}}return instance;
}-(id)copyWithZone:(NSZone *)zone
{return instance;
}-(id)mutableCopyWithZone:(NSZone *)zone
{return instance;
}

验证代码:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <Foundation/Foundation.h>
#import "Singletion.h"int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {// Setup code that might create autoreleased objects goes here.appDelegateClassName = NSStringFromClass([AppDelegate class]);Singletion *singletion = [Singletion mySingletion];Singletion *copySingletion = [singletion copy];Singletion *mutableCopySingletion = [singletion mutableCopy];Singletion *allocSingletion = [[Singletion alloc] init];NSLog(@"%d",singletion == copySingletion);NSLog(@"%d",singletion == mutableCopySingletion);NSLog(@"%d",singletion == allocSingletion);NSLog(@"%d",copySingletion == mutableCopySingletion);}return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

结果:
请添加图片描述

饿汉模式

饿汉模式的特点是在一开始就创建单例对象的实例,提前创建单例对象,并且分配好内存空间。使用时就是将该对象拿出来。


#import "Singletion.h"@implementation Singletion
static id instance = nil;+ (void)load{instance = [[self alloc] init];
}+(instancetype)mySingletion {if (instance == nil) {@synchronized (self) {instance = [[self 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 instance;
}-(id)mutableCopyWithZone:(NSZone *)zone
{return instance;
}

重点就是在程序加载开始,就创建这个单例对象。

判断代码:

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <Foundation/Foundation.h>
#import "Singletion.h"int main(int argc, char * argv[]) {NSString * appDelegateClassName;@autoreleasepool {// Setup code that might create autoreleased objects goes here.appDelegateClassName = NSStringFromClass([AppDelegate class]);Singletion *singletion = [Singletion sharedInstance];Singletion *copySingletion = [singletion copy];Singletion *mutableCopySingletion = [singletion mutableCopy];Singletion *allocSingletion = [[Singletion alloc] init];NSLog(@"%d",singletion == copySingletion);NSLog(@"%d",singletion == mutableCopySingletion);NSLog(@"%d",singletion == allocSingletion);NSLog(@"%d",copySingletion == mutableCopySingletion);}return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

运行结果:

请添加图片描述

单例模式的写法

原本在书中的写法为:
这种方法在多线程调用时,并不能保证成功使用单例模式。

+(instancetype)mySingleton {if (instance == nil) {instance = [[self alloc] init];}return instance;
}

从而我们使用加锁,来保证多线程中也可以创建单例类

加锁写法:

+(instancetype)allocWithZone:(struct _NSZone *)zone
{if(instance == nil) {@synchronized (self) {if(instance == nil) {instance = [super allocWithZone:zone];}}}return instance;
}

还有一个GCD的写法,换一种加锁方式。
GCD写法:

+ (instancetype)allocWithZone:(struct _NSZone *)zone {static dispatch_once_t onceToken = 0;dispatch_once(&onceToken, ^{instance = [super allocWithZone:zone];});return instance;
}

GCD加锁相比于第二个加锁方法,在性能上更有优势。因为它省去了锁操作,代替的是大量的原子操作,直接利用了 lock 的汇编指令,靠底层 CPU 指令来支持的。

dispatch_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。


总结

饿汉模式和懒汉模式都是实现单例模式的一种方法,各有优点。
饿汉模式用空间换取时间,先创建出来,这样再调用时就不需要花时间判断,节省运行时间。
懒汉模式用时间换空间,如果一直不需要创建这个单例,则节约了内存空间。一旦进行判断是否需要创建该实例,则会浪费判断的时间。
我们还需要关注这里通过加锁的方式创建单例,以保证线程安全。这是因为如果到了多线程的环境里,多个进程同时访问单例,该单例模式也有可能返回不同的对象。因此我们可以通过加锁和GCD模式,来实现保证线程安全。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【StringUtils工具类】isEmpty 和 isBlank 的区别

isEmpty 和 isBlank 的区别 前言isEmpty系列StringUtils.isEmpty&#xff08;&#xff09;StringUtils.isNotEmpty&#xff08;&#xff09;StringUtils.isAnyEmpty&#xff08;&#xff09;StringUtils.isNoneEmpty&#xff08;&#xff09;如何插入一段漂亮的代码片 isBank系…

FastDFS架构和原理

FastDFS 项目地址&#xff1a;https://github.com/happyfish100FastDFS 主要功能包括&#xff1a;文件存储&#xff0c;同步和访问&#xff0c;基于高可用的负载均衡。FastDFS 非常适合基于文件服务的站点。FastDFS 有跟踪服务器&#xff08;Tracker Server&#xff09;、存储服…

【数据结构】排序算法---冒泡排序

文章目录 1. 定义2. 算法步骤3. 动图演示4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 冒泡排序&#xff08;英语&#xff1a;Bubble sort&#xff09;是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的…

001、Git开发流程规范

前言 本篇不详细介绍 Git 的使用&#xff0c;仅介绍基于 Git 的开发分支流程规范。 简述 Git 管理中&#xff0c;最重要的一个点就在于分支的管理。在项目开发中&#xff0c;一般涉及到 Git 的相关分支有&#xff1a; master/main: 主分支&#xff0c;版本正式发布的代码都用…

硬件看门狗导致MCU启动时间慢

最近&#xff0c;在项目交付过程中&#xff0c;我们遇到了一个有趣的问题&#xff0c;与大家分享一下。 客户的需求是&#xff1a;在KL15电压上电后&#xff0c;MCU需要在200ms内发送出第一包CAN报文数据。然而&#xff0c;实际测试结果显示&#xff0c;软件需要360ms才能发送…

基于python+django+vue的美术馆预约系统

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

Facebook隐私设置指南:如何更好地保护个人信息

在数字化时代&#xff0c;隐私保护成为了每个互联网用户面临的重要课题。Facebook&#xff0c;作为全球最大的社交网络平台之一&#xff0c;拥有庞大的用户基础和丰富的个人数据。因此&#xff0c;了解和管理Facebook的隐私设置对保护个人信息至关重要。本文将为您提供一份详细…

git push出错Push cannot contain secrets

报错原因&#xff1a; 因为你的代码里面包含了github token明文信息&#xff0c;github担心你的token会泄漏&#xff0c;所以就不允许你推送这些内容。 解决办法&#xff1a; 需要先把代码里面的github token信息删除掉&#xff0c;并且删掉之前的历史提交&#xff0c;只要包…

【推荐100个unity插件之34】在unity中实现和Live2D虚拟人物的交互——Cubism SDK for Unity

最终效果 文章目录 最终效果前言例子中文官网Live2d模型获取下载Live2D Cubism SDK for Unity使用文档限制unity导入并使用Live2D模型1、将SDK载入到项目2、载入模型3、显示模型4、 播放动画 表情动作修改参数眼神跟随看向鼠标效果部位触摸效果摸头效果摸头闭眼效果做成桌宠参考…

Mistral AI 又又又开源了闭源企业级模型——Mistral-Small-Instruct-2409

就在不久前&#xff0c;Mistral 公司在开源了 Pixtral 12B 视觉多模态大模型之后&#xff0c;又开源了自家的企业级小型模型 Mistral-Small-Instruct-2409 &#xff08;22B&#xff09;&#xff0c;这是 Mistral AI 最新的企业级小型模型&#xff0c;是 Mistral Small v24.02 的…

无人机如何突破高海拔高寒飞行环境?

无人机在突破高海拔高寒飞行环境方面&#xff0c;需要解决一系列技术难题和挑战。以下是一些主要的技术手段和策略&#xff1a; 1. 无人机平台设计与优化 增强机体结构&#xff1a;采用轻质高强度的材料&#xff0c;如碳纤维、复合材料等&#xff0c;减轻机身重量&#xff0c…

HomeAssistant显示节假日

先看效果 步骤&#xff1a; 新建卡片时选择“Markdown 卡片”代码在文章最下方&#xff0c;当然你也可以自己修改 点击保存/完成 ### {% if now().hour > 6 and now().hour < 9 -%} 早上好&#xff0c; {%- elif now().hour > 9 and now().hour < 12 -%} 上午好…

ipython里如何用?快速查阅帮助

1、&#xff1f;用于查询函数帮助文档&#xff0c;??用于查询带源码的帮助文档 ?用于搜索内容&#xff0c;*作为通配符。

javascript-原型和原型链

原型 每个函数都有一个默认的原型对象 - prototype ,通过 prototype 我们可以扩展 js 的内置对象。一个函数和它创建的实例共享这个函数的原型属性和方法。实例对象的 constructor 会指向构造函数 原型链 每个实例对象都会有一个隐式原型属性 __proto__,通过 __proto__ 指…

网络安全-shire写任务计划、反弹shell、写私钥、反序列化

目录 一、环境 二、 介绍 三、开始做题 四、写公钥 一、环境 网上自己找 二、 介绍 我们经过前面文章很清楚知道&#xff0c;shiro是将数据存储在内存当中&#xff0c;内存落盘实现一个数据存储&#xff0c;而当其结合python&#xff0c;python将登录的session存储到shiro里…

【隐私计算篇】不经意传输协议(OT/OTE)的进一步补充

1. 背景介绍 关于不经意传输(OT)和不经意传输扩展(OT Extension), 我们在之前的文章《OT&OT扩展(不经意传输扩展)深入浅出》做了详细的说明。但对于OT/OTE的一些技术或者概念&#xff0c;还有一定的内容欠缺&#xff0c;因此本文根据冯登国院士关于安全多方计算协议…

了解快充协议芯片诱骗取电过程

快充协议芯片诱骗取电的过程主要涉及充电器与设备之间的通信和电压协商&#xff0c;以确保安全、快速和高效的充电。这个过程依赖于快充协议芯片&#xff0c;如XSP08Q快充诱骗芯片&#xff0c;它们内置通信模块&#xff0c;能够与供电端的充电器进行握手通信&#xff0c;从而申…

(黑马点评)七、附近商户系列功能实现

7.1 GEO数据结构的认识及其基本使用演示 7.1.1 GEO的介绍 GEO&#xff0c;代表地理坐标。Redis在3.2版本中加入了对GEO的支持&#xff0c;允许存储地理坐标信息&#xff0c;帮助我们根据经纬度来检索数据。常见的命令有&#xff1a; GEOADD&#xff1a;添加一个地理空间信息&a…

Java创建教程!(*  ̄3)(ε ̄ *)

Java 构造函数 Java面向对象设计 - Java构造函数 构造函数是用于在对象创建后立即初始化对象的代码块。 构造函数的结构看起来类似于一个方法。 声明构造函数 构造函数声明的一般语法是 <Modifiers> <Constructor Name>(<parameters list>) throws <…

【Binlog实战】:基于Spring监听Binlog日志

【Binlog实战】&#xff1a;基于Spring监听Binlog日志 binlog的三种模式 MySQL 的二进制日志&#xff08;binlog&#xff09;有三种不同的格式&#xff0c;通常被称为 binlog 模式。这三种模式分别是 Statement 模式、Row 模式和Mixed 模式。 Statement 模式&#xff1a; 在 …