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

【OC】单例模式

文章目录

  • 前言
  • 概念
  • 优缺点
    • 优点
    • 缺点
  • 两种使用模式
    • 懒汉模式
      • 实现代码
      • 运行结果
    • 饿汉模式
      • 实现代码
      • 运行结果
  • 在自定义类方法时的几种常见写法
  • 总结

前言

在之前我们已经学习过单例模式的有关内容,但是只是最简单的单例,无法胜任多线程或者稍微多一点的情况便无法确定单例的唯一性,于是更深度的学习了单例模式

概念

单例模式的定义:一个类有且只有一个实例,并且自行实例化向整个系统提供。

即他用自己内部方法进行创立的唯一对象实例,并且可以被全局访问

优缺点

优点

  1. 全局访问

    单例模式就像一个全局变量,可以通过统一的入口来获取,并可以更方便的共享一些全局的数据与资源

  2. 节省资源

    只创建一次实例,避免了频繁的new/alloc

  3. 控制实例化过程

    通过私有函数和静态方法控制对象的唯一性,保持了数据的唯一性

缺点

  1. 隐藏依赖,增加耦合

    很多地方都直接访问单例,形成一种“隐形依赖”,在更改或替换单例时导致牵一发而动全身

  2. 不利于扩展与测试

    通常通过静态方法提供实例,全局固定,难以继承或替换

  3. 多线程安全问题

    在并发环境下,如果单例初始化没有处理好线程安全(比如加锁或使用 dispatch_once),可能会创建出多个实例,违背单例的初衷,尤其在懒汉式单例实现中,这一点必须特别小心。

两种使用模式

一般来说,创建一个单例之后要保证唯一实例的话要分别改写四种方法,即:用alloc init创建;通过类方法创建;通过copy创建;通过mutableCopy创建

而在改写这四种方法时按照创建时间主要分为两种,即懒汉模式和饿汉模式

懒汉模式

即在我们需要用到这个单例的时候,我们才开始创建这个唯一的实例,通过延迟对象的初始化来节省资源和提高性能,这种也是比较常用的创建单例模式的方式:

实现代码

+ (instancetype)sharedInstance {static Singletion *instance = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{instance = [[super allocWithZone: NULL] init];});return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {return [self sharedInstance];
}
- (id)copyWithZone:(NSZone *)zone {return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {return self;
}
#import <Foundation/Foundation.h>
#import "Singletion.h"int main(int argc, const char * argv[]) {@autoreleasepool {Singletion *s1 = [Singletion sharedInstance];Singletion *s2 = [Singletion sharedInstance];Singletion *s3 = [s1 copy];Singletion *s4 = [s1 mutableCopy];NSLog(@"%d", s1 == s2);NSLog(@"%d", s1 == s3);NSLog(@"%d", s2 == s3);NSLog(@"%d", s1 == s4);}return 0;
}

这里涉及到了一个新的东西,即dispatch_once,经学长博客学习,发现其主要是按照onceToken的值来进行代码执行的

  1. onceToken = 0时,线程执行里面block中的代码
  2. onceToken = -1时,线程跳过block里的代码不执行
  3. onceToken = 其他值时,线程即会被阻塞,等待onceToken的值改变

当线程调用mySingleton方法时,此时 onceToken = 0,调用 block 中的代码,此时 onceToken =其他值。
当其他线程再调用 mySingleton 方法时,onceToken为其他值,线程阻塞。当 block 线程执行完 block之后,onceToken = -1,其他线程不再阻塞,跳过 block。下次再调用这个初始化方法时, block 已经为-1,直接跳过 block

运行结果

请添加图片描述

饿汉模式

饿汉模式指的是我们在一开始加载时就直接创建这个单例对象的实例,使用时再把这个对象拿出来,用到的这种方式不是特别常用,因为性能不如上面的懒汉模式

饿汉有个优点就是,因为这个实例在加载时就已经创建完成,所以其不存在多线程创建的问题,因而一般来说也不需要用dispatch_once或者加锁方法,当然用了也行,不过好像是有点多余的写法

实现代码

#import "Singletion.h"@implementation Singletionstatic Singletion* instance = nil;+ (void)load {instance = [[super allocWithZone: NULL] init];//NSLog(@"Singleton");
}
+ (instancetype)sharedInstance {return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone {if (!instance) {instance = [super allocWithZone: zone];}return instance;
}
- (id)copyWithZone:(NSZone *)zone {return self;
}
- (id)mutableCopyWithZone:(NSZone *)zone {return self;
}
@end
int main(int argc, const char * argv[]) {@autoreleasepool {Singletion *s1 = [Singletion sharedInstance];Singletion *s2 = [Singletion sharedInstance];Singletion *s3 = [s1 copy];Singletion *s4 = [s1 mutableCopy];NSLog(@"%d", s1 == s2);NSLog(@"%d", s1 == s3);NSLog(@"%d", s2 == s3);NSLog(@"%d", s1 == s4);}return 0;
}

运行结果

请添加图片描述

在自定义类方法时的几种常见写法

首先我们知道饿汉一般是不需要担心其线程安全问题的,所以一般只考虑懒汉模式的几种写法,主要有两种

在懒汉模式中一般有两种写法,分别是GCD和加锁的写法,GCD的写法是现在写法更推荐的,因为其性能极快且第一次使用后后续基本无开销,而使用加互斥锁@synchronized的方式性能较慢,且每次使用时都有锁的开销所以不常用

GCD的写法在上面已经给出,下面我给出使用加锁方式的代码:

static Singletion* 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;
}

总结

总而言之,懒汉模式一般用于需要延迟加载实例的情况,可以节省资源,提高性能,但是需要考虑线程安全的问题;饿汉模式适用于需要简单实现和线程安全的情况,但是不支持延迟加载,在程序开始时便加载完成了


文章转载自:

http://skOYKh12.ywpwq.cn
http://k0B2t7CF.ywpwq.cn
http://mQhRpmJ4.ywpwq.cn
http://KqrLeY3u.ywpwq.cn
http://I04vJ2M7.ywpwq.cn
http://behHOz3N.ywpwq.cn
http://qo6EqsNR.ywpwq.cn
http://8LxLm74T.ywpwq.cn
http://1gSgBFGt.ywpwq.cn
http://LGibGDes.ywpwq.cn
http://YrgPmDk2.ywpwq.cn
http://P6LDH2px.ywpwq.cn
http://H0Gm9MzI.ywpwq.cn
http://motWpDqf.ywpwq.cn
http://oePcTWcD.ywpwq.cn
http://IdwTXCuD.ywpwq.cn
http://vZYp18AQ.ywpwq.cn
http://ET4xZkjl.ywpwq.cn
http://LLqGO8DK.ywpwq.cn
http://kLMp1E6K.ywpwq.cn
http://k4bVh3KF.ywpwq.cn
http://oUHkAadH.ywpwq.cn
http://CHG1qLZr.ywpwq.cn
http://7TAWK29K.ywpwq.cn
http://PK0Vw65c.ywpwq.cn
http://zSeuXHAJ.ywpwq.cn
http://Di1iMVKA.ywpwq.cn
http://54KUNRdI.ywpwq.cn
http://WQzcez9e.ywpwq.cn
http://wWcLOsGw.ywpwq.cn
http://www.dtcms.com/a/380218.html

相关文章:

  • 【数据结构】LRU Cache
  • 阅读翻译Discovering Modern C++之5.2.3 A `const`-Clean View Example
  • MUSIC, Maximum Likelihood, and Cramer-Rao Bound
  • APT32F0042F6P6 32位微控制器(MCU)单片机 APT爱普特微电子 芯片核心解析
  • react3面试题
  • LeetCode 344.反转字符串
  • 【C++】list模拟实现全解析
  • C++动态规划算法:斐波那契数列模型
  • 第六章:AI进阶之------python的变量与赋值语句(二)
  • 传统项目管理流程有哪些?深度分析
  • 导购电商平台的服务治理体系构建:熔断、限流与降级机制实现
  • Axios 中设置请求头
  • 十四十五. 图论
  • Transporter App 使用全流程详解:iOS 应用 ipa 上传工具、 uni-app 应用发布指南
  • 缺失数据处理全指南:方法、案例与最佳实践
  • 【后端】Java封装一个多线程处理任务,可以设置任务优先级优先插队处理,并且提供根据任务ID取消任务
  • 数据通信学习
  • Coze源码分析-资源库-创建知识库-前端源码-核心组件
  • GEO 优化工具:让品牌被 AI 主动推荐的关键!
  • 调用京东商品详情API接口时,如何进行性能优化?
  • 鸿蒙审核问题——折叠屏展开态切换时,输入框内容丢失
  • JAiRouter GitHub Actions 自动打包发布镜像到 Docker Hub 技术揭秘
  • 破壁者指南:内网穿透技术的深度解构与实战方法
  • TOGAF——ArchiMate
  • 吃透 Vue 样式穿透:从 scoped 原理到组件库样式修改实战
  • Linux网络:初识网络
  • 【Docker-Nginx】通过Docker部署Nginx容器
  • 测试es向量检索
  • 统计与大数据分析专业核心工具指南
  • Qtday2作业