【iOS】单例模式

目录

  • 前言
  • 单例模式
    • 认识单例模式
    • 单例模式的特点及使用情景
    • 单例模式的使用
      • 单例模式的实现步骤:
      • 完整代码
  • 总结

前言

  在进行大项目编写之前,开始对前面比较重要的知识进行回顾和重新学习,单例模式在软件开发设计中是比较重要的,尤其是它的初始化,笔者重新学习了单例模式并作该笔记。

单例模式

认识单例模式

在百度中,单例模式的定义如下:

数学与逻辑学中,singleton定义为“有且仅有一个元素的集合”。 单例模式最初的定义出现于《设计模式》(艾迪生维斯理, 1994):“保证一个类仅有一个实例,并提供一个访问它的全局访问点。”
Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”

在OC中,单例模式(Singleton Pattern)也如此,其核心目的是确保一个类只有一个实例,并提供一个全局访问点来获取这个实例

关于对单例模式更通俗易懂的解释,我读到掘金上的一篇文章是这样举例的:

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

在OC中,最经典的单例模式就是xcode里自带的UIApplication,其在应用程序的整个生命周期中只会创建一个实例,如果在编译过程中进行新的创建就会报错,如下:

UIApplication *app = [[UIApplication alloc]init];
//或者UIApplication *app = [[UIApplication new];

在这里插入图片描述
“There can only be one UIApplication instance”:该应用程序中只能有一个UIApplication实例。

单例模式的特点及使用情景

单例模式的关键特点:

  • 唯一性:单例类在应用程序的整个生命周期中只会创建一个实例(例如UIApplication)。
  • 全局访问点:单例类提供了一个全局的访问点,使得这个唯一实例可以被整个应用程序访问(例如:[UIApplication sharedApplication];)。
  • 线程安全:在多线程环境中,单例模式的实现需要确保实例的创建是线程安全的,以防止创建多个实例。
  • 延迟初始化:单例实例通常在第一次被引用时才创建,这样可以延迟初始化的开销,提高系统启动速度。

常见的适合使用单例模式的情况:

  1. 当你需要在整个程序的运行周期中,让一个类始终保持同一个实例。则必须使用单例模式;(例如 [UIApplication sharedApplication])
  2. 在生命周期中管理数据,例如自定义的管理中心或者[NSUserDefaults standardUserDefaults];
  3. 为了节省内存,部分类可以使用单例模式,例如多处使用的图片等。

单例模式的使用

单例模式的实现步骤:

1.私有化初始化方法:将初始化方法设为私有,以防止外部通过 new、alloc 或其他方式创建类的实例。

// 私有化初始化方法
- (instancetype)init {
    if ((self = [super init])) {
        // 执行初始化操作
    }
    return self;
}

// 使init方法不可用
+ (instancetype)new {
    return [SingleView sharedSingleView];
}

2.提供一个公共的类方法:提供一个公共的类方法,通常命名为 shared+类名,用于返回类的唯一实例。

//公共的类方法
+ (SingleView *)sharedSingleView {
    //声明一个SingleView类型的静态变量single,初始值为nil。静态变量的作用域限定在方法内部,但它的生命周期是整个应用程序,这意味着它只会被初始化一次。
    static SingleView *single = nil;
    
    //声明了一个dispatch_once_t类型的静态变量onceToken。dispatch_once是一个宏,用于确保代码块只执行一次。onceToken用于跟踪代码块是否已经执行过
    static dispatch_once_t onceToken;
    
    //执行dispatch_once宏,传入onceToken变量的地址和要执行的代码块。如果onceToken还没有被标记为已执行,代码块将被执行一次,并将onceToken标记为已执行,这样代码块就不会再次执行。
    dispatch_once(&onceToken, ^{
        //创建SingleView类的一个新实例,并将其赋值给single变量。super关键字表示调用父类的alloc方法,这里是NSObject类的方法。
        single = [[super alloc] init]; //等价于 single = [super new];
    });
    return single;
}

//重写allocWithZone:方法以确保当使用alloc或new创建对象时,总是返回单例对象。
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [SingleView sharedSingleView];
}

3.使用静态实例变量:存储类的唯一实例。

static SingleView *single = nil;

4.使用 dispatch_once:使用 dispatch_once 来确保实例化代码只执行一次,这保证了线程安全和单例的唯一性。

dispatch_once(&onceToken, ^{
        //创建SingleView类的一个新实例,并将其赋值给single变量。super关键字表示调用父类的alloc方法,这里是NSObject类的方法。
        single = [[super alloc] init]; //等价于 single = [super new];
    });

完整代码

//  SingleView.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SingleView : NSObject<NSCopying>

+ (SingleView *)sharedSingleView;

@end

NS_ASSUME_NONNULL_END
//  SingleView.m

#import "SingleView.h"

@implementation SingleView

// 私有化初始化方法
- (instancetype)init {
    if ((self = [super init])) {
        // 执行初始化操作
    }
    return self;
}

// 使init方法不可用
+ (instancetype)new {
    return [SingleView sharedSingleView];
}

//公共的类方法
+ (SingleView *)sharedSingleView {
    //声明一个SingleView类型的静态变量single,初始值为nil。静态变量的作用域限定在方法内部,但它的生命周期是整个应用程序,这意味着它只会被初始化一次。
    static SingleView *single = nil;
    
    //声明了一个dispatch_once_t类型的静态变量onceToken。dispatch_once是一个宏,用于确保代码块只执行一次。onceToken用于跟踪代码块是否已经执行过
    static dispatch_once_t onceToken;
    
    //执行dispatch_once宏,传入onceToken变量的地址和要执行的代码块。如果onceToken还没有被标记为已执行,代码块将被执行一次,并将onceToken标记为已执行,这样代码块就不会再次执行。
    dispatch_once(&onceToken, ^{
        //创建SingleView类的一个新实例,并将其赋值给single变量。super关键字表示调用父类的alloc方法,这里是NSObject类的方法。
        single = [[super alloc] init]; //等价于 single = [super new];
    });
    return single;
}

//重写allocWithZone:方法以确保当使用alloc或new创建对象时,总是返回单例对象。
+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    return [SingleView sharedSingleView];
}

//重写copyWithZone:方法以确保当尝试复制单例对象时,总是返回单例对象本身。
+ (id)copyWitnZone:(struct _NSZone *)zone {
    return [SingleView sharedSingleView];
}

//重写mutableCopyWithZone:方法以确保当尝试创建可变副本时,总是返回单例对象本身。
+ (id)mutableCopyWithZone:(struct _NSZone *)zone {
    return [SingleView sharedSingleView];
}

//重写copyWithZone:方法以确保当尝试复制单例对象时,总是返回单例对象本身。
- (id)copyWithZone:(NSZone *)zone{
    return  [SingleView sharedSingleView];
}

//重写mutableCopyWithZone:方法以确保当尝试创建可变副本时,总是返回单例对象本身。
- (id)mutableCopyWithZone:(NSZone *)zone{
    return [SingleView sharedSingleView];
}

@end

总结

总的来说,单例模式是一种常用的软件开发模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。使用单例模式时要注意:
1.确保线程安全:单例的创建应该在多线程环境中是安全的。==使用 dispatch_once ==可以确保实例化代码只执行一次,从而避免多线程问题。
2.私有化构造函数:将类的构造函数标记为私有,以防止外部通过构造函数创建类的实例。
3.提供一个公共的获取实例的方法:提供一个公共的类方法,如 shared+类名。用于获取类的唯一实例。
4.管理资源和内存:单例对象通常在整个应用程序的生命周期内都存在,因此需要小心管理它所持有的资源,确保没有内存泄漏。
5.防止滥用单例模式:单例模式不适用于所有场景。它适用于那些确实只需要一个实例的类,如配置管理器、日志记录器等。滥用单例模式会增加系统的耦合度。
6.考虑使用协议和委托:如果单例需要与其他对象交互,考虑使用协议和委托而不是直接引用,以减少耦合。

参考文档:iOS 设计模式之单例模式
     ios 单例重新初始化方法

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

EFI引导模式下配置Windows和Linux双系统共存

这几天在VirtualBox虚机里玩Modular MAX下的LLama3大模型&#xff0c;实在受不了这执行速度&#xff0c;于是下决心把Ubuntu系统安装在硬盘上跟Windows11做双系统共存。之前在传统BIOS引导模式下做过不少次双系统引导&#xff0c;EFI模式下第一次做&#xff0c;加之windows系统…

计算机毕业设计 大学志愿填报系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

大数据-129 - Flink CEP 详解 Complex Event Processing - 复杂事件处理

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

91、K8s之ingress上集

一、Ingress service模式&#xff1a; loadbalance NodePort&#xff1a;每个节点都会有一个指定的端口 30000-32767 内网 clusterip&#xff1a;默认模式&#xff0c;只能pod内部访问 externalName&#xff1a;需要dns提供域名 1.1、对外提供服务的ingress service&…

线性规划------ + 案例 + Python源码求解(见文中)

目录 一、代数模型&#xff08;Algebraic Models&#xff09;详解1.1什么是代数模型&#xff1f;1.2代数模型的基本形式1.3 安装所需要的Python包--运行下述案例1.4代数模型的应用案例案例 1&#xff1a;市场供需平衡模型Python求解代码Python求解结果如下图&#xff1a; 案例 …

【快速解决】搭建VUE+VScode+elementUI开发环境,Vue环境配置

目录 1、通过这个之间下载node.js&#xff08;全选next即可&#xff09; 2、winr检验是否安装成功&#xff08;运行下面两个命令即可&#xff09; 3、将下面我给你的这个压缩包解压&#xff0c;然后放到空间足够的磁盘里面 4、【重点】设置环境变量 第一个变量路径里面长这…

ubuntu中QT+opencv在QLable上显示摄像头

ubuntu中QTopencv在QLable上显示摄像头 饭前的一篇文章吧&#xff0c;写完吃饭走 图像在机器视觉中的重要性是不可忽视的。机器视觉是指计算机利用图像处理技术进行图像识别、分析和理解的科学与技术领域。图像是机器视觉的输入数据&#xff0c;通过分析和处理图像&#xff0…

HTML中的文字与分区标记

1.font标记&#xff1a;用来设置文字的字体&#xff0c;大小&#xff0c;颜色&#xff0c;等属性 <!--font:font标记用来设置字体大小颜色属性size:设置字号&#xff0c;默认是3号&#xff0c;1表示4号&#xff0c;-1表示2号&#xff0c;取值范围是[1,7]或[-7,-1]color:设置…

Docker零基础入门

参考课程https://www.bilibili.com/video/BV1VC4y177re/?vd_source=b15169a302bee35f484245aecc69d4dd 参考书籍Docker 实践 - 面向 AI 开发人员的 Docker 实践 (dockerpractice.readthedocs.io) 1. 什么是Docker 1.1. Docker起源 随着计算机的发展,计算机上已经可以运行多…

C++ | Leetcode C++题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {sort(people.begin(), people.end(), [](const vector<int>& u, const vector<int>& v) …

第108集《大佛顶首楞严经》

请打开讲义241面。我们讲到嗅报&#xff0c;鼻根当中嗅的功能。 本根发相 发明二相&#xff1a;一者通闻&#xff0c;被诸恶气&#xff0c;熏极心扰。二者塞闻&#xff0c;气掩不通&#xff0c;闷绝于地。 以鼻根造业到无间地狱以后&#xff0c;他有二种受苦的相状&#xf…

[C++] 剖析多态的原理及实现

文章目录 多态的概念及定义编译时多态&#xff08;静态多态&#xff09;运行时多态&#xff08;动态多态&#xff09;动态多态的原理示例&#xff1a;运行时多态 两种多态的区别 多态的实现基本条件虚函数虚函数的重写与覆盖虚函数重写的其他问题协变析构函数的重写 C11 中的 o…

引领智能家居新风尚,WTN6040F门铃解决方案——让家的呼唤更动听

在追求高效与便捷的智能家居时代&#xff0c;每一个细节都承载着我们对美好生活的向往。WTN6040F&#xff0c;作为一款专为现代家庭设计的低成本、高性能门铃解决方案&#xff0c;正以其独特的魅力&#xff0c;悄然改变着我们的居家生活体验。 芯片功能特点&#xff1a; 1.2.4…

直流电源纹波怎么测量?示波器的探头和带宽如何选择?

对于电源工程师来说&#xff0c;精确测量电源纹波是一项基本技能。本文将详细介绍直流电源纹波测试时的注意事项&#xff0c;包括示波器探头的选择、带宽设置、时基选择&#xff0c;确保精准测量直流电源纹波。 一、选择合适的示波器带宽 为了避免电路的高频噪声影响电源纹波的…

基于树莓派ubuntu20.04的ros-noetic小车

目录 一、小车的架构 1.1 总体的概述 1.2 驱动系统 1.3 控制系统 二、驱动系统开发 2.1 PC端Ubuntu20.04安装 2.2 树莓派Ubuntu20.04安装 2.3 PC端虚拟机设置静态IP 2.4 树莓派设置静态IP 2.5 树莓派启动ssh进行远程开发 2.5 arduino ide 开发环境搭建 2.5.1 PC…

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

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

【数据结构】8——图3,十字链表,邻接多重表

数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表 文章目录 数据结构8——图3&#xff0c;十字链表&#xff0c;邻接多重表前言一、十字链表结构例子 复杂例子 二、邻接多重表&#xff08;Adjacency Multilist&#xff09;例子 前言 除了之前的邻接矩阵和邻接表 …

在k8s中,客户端访问服务的链路流程,ingress--->service--->deployment--->pod--->container

图片来源&#xff1a;自己画的 ingress是一个API资源。 客户端访问ingress的不同url ingress给客户端返回不同的服务。 就和nginx反向代理服务器一样。 根据不同的url&#xff0c;给客户端返回不同的服务。 -----------------------------------------------------------…

MySql基础-单表操作

1. MYSQL概述 1.1 数据模型 关系型数据库 关系型数据库(RDBMS)&#xff1a;建立在关系模型基础上&#xff0c;由多张相互连接的二维表组成的数据库。 特点&#xff1a; 使用表存储数据&#xff0c;格式统一&#xff0c;便于维护 使用SQL语言操作&#xff0c;标准统一&…

班迪录屏和这三款录屏工具,一键操作,太方便了!

嘿&#xff0c;小伙伴们&#xff01;今天我要跟大家分享几款超棒的录屏工具&#xff0c;它们绝对是我们在工作和学习中不可或缺的好帮&#xff1b;这些工具功能强大且操作简单&#xff0c;下面就让我来详细介绍一下它们的使用体验和好用之处吧&#xff01; 班迪录屏工具使用体…