「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模式,来实现保证线程安全。
相关文章:
「iOS」——单例模式
iOS学习 前言单例模式的概念单例模式的优缺点单例模式的两种模式懒汉模式饿汉模式单例模式的写法 总结 前言 在一开始学习OC的时候,我们初步接触过单例模式。在学习定时器与视图移动的控件中,我们初步意识到单例模式的重要性。对于我们需要保持的控件&a…...
Selenium自动化测试面试必备:高频面试题及答案整理
自动化测试已经成为现代软件测试中不可或缺的一部分。在自动化测试中,Selenium是最受欢迎的工具之一,因为它可以模拟用户与Web应用程序的交互。因此,对于许多测试工程师来说,熟练掌握Selenium框架是非常重要的。如果你正在寻找一份…...
kettle 数据库迁移 使用分页原理实现 数据库mysql
使用 kettle 9.0 先修改配置文件: C:\Users\xx\.kettle 新增如下配置,解决mysql 空字符串 自动转 null bug KETTLE_EMPTY_STRING_DIFFERS_FROM_NULLY git地址: GitHub - 2292011451/kettle_tool 第一步: 先把要迁移的表进行读取,循环查询每个表的最大数量以及页数,追加到…...
量化回测bt框架,策略类bt.Strategy详解,不是backtrader!提供bt双均线策略示例,比backtrader还简单
前言 也不说那么多了,要用到bt,肯定也知道他是干嘛的,,给博主点点关注点点赞!!!这样博主才能更新更多免费的教程,不然就直接丢付费专栏里了 正文 bt.Strategy 是 bt 库中用于定义交…...
网络安全宣传周 | DNS安全威胁与应对措施分享
随着网络技术的快速发展和国际形势的日趋复杂,网络安全问题日益凸显,网络安全威胁开始呈现多样化、隐蔽化、高频化、系统化的发展态势。黑客攻击、网络诈骗、数据泄露等事件频发,不仅威胁到个人隐私和财产安全,也严重影响到国家政…...
【图书介绍】《Altium Designer 24入门与案例实践(视频教学版)》
本书重点 配套资源丰富,包括示例源文件、PPT课件、教学视频、电子教案、课程标准、教学大纲、模拟试题、作者微信群答疑服务。 内容简介 《Altium Designer 24入门与案例实践:视频教学版》以当前**的板卡级设计软件Altium Designer 24为基础,全面讲述…...
mysql事务的隔离级别学习
事务的隔离级别: ⅰ. 读未提交 ⅱ. 对已提交 (解决 脏读) ⅲ. 可重复读 (解决 不可重复读) ⅳ. 串行化 (解决 脏读 不可重复读 幻读 问题 ) 隔离级别分类如下,在不同的隔离级别下可能产生不…...
Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用增强扩展(text2sql)
前言 我在上一篇文章中《Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用(text2sql)》 利用langchain 中create_sql_agent 创建一个数据库代理智能体,但是实测中发现,使用 create_sql_agent 在对话中&#x…...
rapidocr 提取汇总
rapidocr介绍 A cross platform OCR Library based on OnnxRuntime. 以下资料是根据RapidOCR获得2024中国互联网发展创新与投资大赛(开源)二等奖整理汇编的 支持识别的文种如下: 中、英、日、韩、中文繁体、泰卢固文、卡纳达文、泰米尔文、拉丁文、 阿拉伯字母 、斯拉夫字…...
Linux:用户账号管理和组账号管理
用户账号管理 账号控制总述 用户账户 作用: 1.可以登陆操作系统 2.不同的用户具备不同的权限 唯一标识:UID(编号从0开始的编号,默认最大60000)zhangsan(UID 1200) 管理员root的UID:永远为0 系统用户(为程…...
MyBatis-Plus分页查询、分组查询
目录 准备工作1. 实体类2. Mapper类3. 分页插件4. 数据 分页查询1. 使用条件构造器2. 使用自定义sql 分组查询1. 分组结果类2. 自定义sql3. 测试类 准备工作 1. 实体类 对地址字段address使用字段类型转换器,将List转为字符串数组保存在数据库中 package com.exa…...
2024年9月HarmonyOS鸿蒙应用开发者高级认证全新题库(覆盖99%考题)
一个小时通过鸿蒙高级认证 1、在开发 Harmony0S 应用工程时, 随着业务的发展,现在需要创建一个模块, 关于在 DevEco Studio 中创建 Module , 下列选项哪种方式是错误的? 必对 在 hvigor 目录下,单击鼠标右键…...
大工程师插件下载 官方地址
https://download.3dsource.cn/3DSource_Client.exe...
rtems 5.3 qemu realview_pbx_a9 环境搭建:生成 rtems arm 工具链
前言 rtems 是一款比较优秀的 RTOS,官方网址 https://www.rtems.org/ 当前 rtems 最新发布的版本:rtems-5.3 版本, 下载地址 https://ftp.rtems.org/pub/rtems/releases/5/5.3/ rtems 支持的 平台也是比较多的,当前支持 STM32F4…...
【算法】栈与模拟
【ps】本篇有 5 道 leetcode OJ。 目录 一、算法简介 二、相关例题 1)删除字符串中的所有相邻重复项 .1- 题目解析 .2- 代码编写 2)比较含退格的字符串 .1- 题目解析 .2- 代码编写 3)基本计算器 II .1- 题目解析 .2- 代码编写 4&…...
【Django】Django AI 聊天机器人项目:基于 ChatGPT 的 Django REST API
Django AI 聊天机器人项目:基于 ChatGPT 的 Django REST API 本文档将介绍如何使用 Django 和 Django REST Framework 构建一个 AI 聊天机器人项目,并结合 OpenAI 的 GPT 模型提供代码解释服务。步骤包括创建 Django 项目、配置 API、与 OpenAI 集成&am…...
System.out源码解读——err 和 out 一起用导致的顺序异常Bug
前言 笔者在写一个小 Demo 的过程中,发现了一个奇怪的问题。问题如下: // 当 flagtrue 时打印 a1 ;当 flagfalse 时打印 a2。 public static void main(String[] args) {boolean flag false;for (int i 0; i < 10; i) {if (flag) {Sys…...
汽车软件开发之敏捷开发
一、前言 目前汽车电子产品,特别是汽车几大域控(如:智能座舱、智能驾驶、智能网联、车身控制)市场竞争激烈,消费者对汽车的需求逐渐多元化和个性化,用户对座舱和智驾产品的要求也越来越高。他们不仅要求产…...
ListBox显示最新数据、左移和右移操作
1、程序 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using static Sys…...
mysql实用系列:日期格式化
在MySQL中,你可以使用DATE_FORMAT()函数来格式化日期。DATE_FORMAT() 函数通常用于格式化 DATETIME 或 TIMESTAMP类型的字段。这个函数允许你按照指定的格式来显示日期和时间。下面是一些常见的日期格式化的例子: 显示年-月-日: SELECT DATE_…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
