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

iOS学习


单例

优点:

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

缺点:

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

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

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

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

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

懒汉模式:

objectivec 复制代码
#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;
}

饿汉模式:

objectivec 复制代码
#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;
}

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

加锁写法:

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

GCD写法:

objective-c 复制代码
+ (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) - 实现协议中定义的方法

回顾一下使用:

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

objectivec 复制代码
@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:方法进行检查

objectivec 复制代码
- (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];
    }
}

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

objectivec 复制代码
#import <UIKit/UIKit.h>
#import "SecondViewController.h"

@interface FirstViewController : UIViewController <SecondViewControllerDelegate>

@property (nonatomic, strong)UITextField* textField;

@end

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

objectivec 复制代码
- (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];
    //将委托对象(代理)设置为FirstViewController
    secondViewController.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

objectivec 复制代码
@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更加的方便。

三种工厂模式

通过给定参数来返回对应的实例,完全对用户隐藏其实现的原理。

优势:易于替换,面向抽象编程

依赖倒置原则

相关推荐
歪歪1001 小时前
HTML 如何转 Markdown
开发语言·chrome·python·程序人生·html
小指纹1 小时前
cf--思维训练
c++·算法·macos·ios·objective-c·cocoa
小坏坏的大世界1 小时前
C++中多线程和互斥锁的基本使用
开发语言·c++
努力奋斗12 小时前
VUE-第二季-02
前端·javascript·vue.js
路由侠内网穿透2 小时前
本地部署 SQLite 数据库管理工具 SQLite Browser ( Web ) 并实现外部访问
运维·服务器·开发语言·前端·数据库·sqlite
王者鳜錸2 小时前
PYTHON从入门到实践-18Django模版渲染
开发语言·python·django
Sane2 小时前
react函数组件怎么模拟类组件生命周期?一个 useEffect 搞定
前端·javascript·react.js
Hard but lovely2 小时前
C++ STL--> vector的模拟实现!
开发语言·c++
hweiyu002 小时前
IDEA搭建GO环境
开发语言·后端·golang·intellij-idea·idea·intellij idea
gnip3 小时前
可重试接口请求
前端·javascript