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

「iOS」————单例与代理

iOS学习

  • 单例
  • 代理
      • 代理模式的原理
      • 代理的循环引用
  • 设计模式


单例

优点:

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

缺点:

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

系统为我们提供的单例类:

UIApplication(应用程序实例类)
NSNotificationCenter(消息中心类)
NSFileManager(文件管理类)
NSUserDefaults(应用程序设置)
NSURLCache(请求缓存类)
NSHTTPCookieStorage(应用程序cookies池)

分为懒汉模式和饿汉模式。懒汉模式是需要时创建,饿汉模式是程序启动时创建,用的时候拿出来

实现单例模式只需要改写四种方法:alloc init方法,类方法,copy方法,mutable Copy方法。

懒汉模式:

#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 "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;
}

由于多线程的原因,实现真正的单例模式需要加锁,有以下两种方法:

加锁写法:

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

GCD写法:

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

dispatch_once 主要是根据 onceToken 的值来决定怎么去执行代码。

1.当 onceToken = 0 时,线程执行 dispatch_once 的 block 中代码;

2.当 onceToken = -1 时,线程跳过 dispatch_once 的 block 中代码不执行;

3.当 onceToken 为其他值时,线程被阻塞,等待 onceToken 值改变。

注意:此处使用GCD写法要好过加锁写法。GCD不仅有更小的开销,更好的性能,同时还不需要手动管理锁。

代理

协议是多个类(或者对象)之间协商的一个公共接口,提供了一系列方法的声明给类们使用;代理是协议的一个典型应用机制。代理模式的核心思想就是通过代理接口分离使用者和服务提供者,降低了模块之间的耦合度。

在这里插入图片描述

协议的编写规范:

  1. 一般情况下, 当前协议属于谁, 我们就将协议定义到谁的头文件中

  2. 协议的名称一般以它属于的那个类的类名开头, 后面跟上protocol或者delegate

  3. 协议中的方法名称一般以协议名称protocol之前的作为开头

  4. 一般情况下协议中的方法会将触发该协议的对象传递出去

  5. 一般情况下一个类中的代理属于的名称叫做 delegate

  6. 当某一个类要成为另外一个类的代理的时候, 一般情况下在.h中用@protocol 协议名称;告诉当前类 这是一个协议.在.m中用#import真正的导入一个协议的声明

注意:

  1. 协议不能声明成员变量,不能写实现
  2. 只要父类遵守了某个协议,那么子类也遵守
  3. 协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明

协议中有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)

  • @required:这个方法必须要实现(若不实现,编译器会发出警告)
  • @optional:这个方法不一定要实现

代理模式的原理

在iOS中代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。

组成部分:

  1. 协议(Protocol) - 定义了代理需要实现的方法
  2. 委托方(Delegator) - 持有代理对象并在适当时机调用代理方法
  3. 代理方(Delegate) - 实现协议中定义的方法

请添加图片描述

回顾一下使用:

首先我们定义一个协议,并且设置定义委托方

@protocol SecondViewControllerDelegate <NSObject>- (void)didUpdateText: (NSString *)text;@end@interface SecondViewController : UIViewController//定义委托方
@property (nonatomic, weak) id<SecondViewControllerDelegate> delegate;
@property (nonatomic, strong) UITextField *textField;@end

在委托方的.m文件中,合适的地方触发委托方法。要注意确保该委托对象实现了委托方法。所以可以使用respondsToSelector:方法进行检查

- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor lightGrayColor];self.textField = [[UITextField alloc] initWithFrame: CGRectMake(20, 100, 200, 30)];self.textField.center = self.view.center;self.textField.borderStyle = UITextBorderStyleRoundedRect;//self.textField.text = @"placeholder";self.textField.delegate = self;[self.view addSubview: self.textField];
}
//触发委托方法
- (void)textFieldDidChangeSelection:(UITextField *)textField {if ([self.delegate respondsToSelector: @selector(didUpdateText:)]) {[self.delegate didUpdateText: textField.text];}
}

再定义代理方:首先要遵循委托方的协议。

#import <UIKit/UIKit.h>
#import "SecondViewController.h"@interface FirstViewController : UIViewController <SecondViewControllerDelegate>@property (nonatomic, strong)UITextField* textField;@end

其次要实现委托协议,并且将自己设置会委托方的代理。即.delegare = self。

- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor lightGrayColor];self.textField = [[UITextField alloc] initWithFrame: CGRectMake(20, 100, 200, 30)];self.textField.center = self.view.center;self.textField.borderStyle = UITextBorderStyleRoundedRect;[self.view addSubview: self.textField];UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: @selector(pushSecondController)];[self.view addGestureRecognizer: tapGesture];
}- (void)pushSecondController {SecondViewController* secondViewController = [[SecondViewController alloc] init];//将委托对象(代理)设置为FirstViewControllersecondViewController.delegate = self;[self.navigationController pushViewController: secondViewController animated: YES];
}//实现委托协议
- (void)didUpdateText:(NSString *)text {//协议传值self.textField.text = text;
}

代理的循环引用

一下代码会发生代理的循环引用:B强引用A,而A的delegate属性指向B,这里的delegate是用strong修饰的,所以A也会强引用B。因此,通常情况下,我们都是用弱引用weak来修饰delegate

@protocol ClssADelegate
- (void)eat;
@end@interface ClassA : UIViewController
@property (nonatomic, strong) id  delegate;//改为弱引用weak
@end//ClassB:
@interface ClassB ()
@property (nonatomic, strong) ClassA *classA;
@end@implementation ClassB
- (void)viewDidLoad {[super viewDidLoad];self.classA = [[ClassA alloc] init];self.classA.delegate = self;
}

请添加图片描述

下面我们看几种传值方式的优缺点:

请添加图片描述

设计模式

此处简介一下设计模式,后期再继续补充。

KVO/通知 -------> 观察者模式

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
优势:解耦合
接口隔离原则、开放-封闭原则

KVC --------> KVC模式
单例模式

利用应用程序只有一个该类的实例对象这一特殊性来实现资源共享。
优势:使用简单,延时求值,易于跨模块
劣势:这块内存知道程序退出时才能释放
单一职责原则
举例:[UIApplication sharedApplication]。

代理模式

委托方将不想完成的任务交给代理方处理,并且需要委托方通知代理方才能处理。
优势: 解耦合
开放-封闭原则
举例:tableview的数据源和代理

策略模式

策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
优势:使算法的变化独立于使用算法的用户
接口隔离原则、多用组合,少用继承、针对接口编程,而非实现
举例:账号密码输入格式的判断、NSArray的sortedArrayUsingSelector等等

MVC模式

将程序书写分为三层,分别为模型、视图、控制器,每层都有各自的职责完成各自的工作。
优势: MVC模式使系统,层次清晰,职责分明,易于维护
对扩展开放-对修改封闭

MVVM模式

用于解决MVC模式下C层代码冗杂的情况(过多的网络请求以及业务逻辑处理)而出现的MVVM模式,其相比于MVC多了一层ViweModel(业务处理和数据转化)层,专门用于处理数据。
当功能简单时,MVVM反而会增加很多代码,所以对于简单的功能,MVC更加的方便。

三种工厂模式

通过给定参数来返回对应的实例,完全对用户隐藏其实现的原理。
优势:易于替换,面向抽象编程
依赖倒置原则

http://www.dtcms.com/a/317746.html

相关文章:

  • 使用PHP与Apache实现服务器端文件管理
  • 19day-人工智能-机器学习-分类算法-决策树
  • docker 部署Bedrock Connector
  • 希尔排序:高效插入排序的进阶之道
  • 从零开始部署Qwen3-8b大模型到本地
  • Centos 安装 redis
  • 17_INIT_WORKLinux内核模块
  • prefetch 下载 GEO 数据注意事项
  • 设计模式—桥梁模式(Bridge)
  • 移动端跨平台框架(支持Harmony、iOS、Android)
  • 《第十篇》深入解析 `MilvusKBService`:基于 Milvus 的知识库服务实现
  • 在线计算“蛋白-蛋白复合物”的MM/GBSA
  • KMP算法详解及应用指南
  • Cartographer安装测试与模块开发(四)--Cartographer纯定位思路
  • 【代码随想录day 12】 力扣 102.107.199. 二叉树的层序遍历
  • 数据库设计方法详解
  • Spring之【初识AOP】
  • 应急响应linux
  • 英伟达算力卡巅峰对决:H100、A100与消费级显卡哪里找?
  • 数语科技登陆华为云商店,助力企业释放数据潜能
  • day20|学习前端
  • JavaScript 基础语法
  • 频数分布表:数据分析的基础工具
  • Adobe Analytics 数据分析平台|全渠道客户行为分析与体验优化
  • Qt 容器类详解:顺序容器与关联容器及其遍历方法
  • [LVGL] 配置lv_conf.h | 条件编译 | 显示屏lv_display
  • 组合模式(Composite Pattern)及其应用场景
  • 基于spring boot的个人博客系统
  • tkwebview-tkinter的web视图
  • 解决云服务器端口无法访问的方法