单例
优点:
- 全局访问:单例模式确保一个类只有一个实例,并提供全局访问点,方便在整个应用中共享数据或功能。
- 节省资源:由于只创建一个实例,可以减少内存开销,避免重复创建相同对象。
- 控制实例化:通过私有构造函数,防止外部代码创建多个实例,确保数据一致性。
缺点:
- 隐藏依赖:使用单例可能导致代码中隐藏的依赖关系,增加了代码的耦合性,降低了可测试性。
- 难以扩展:单例模式不易于扩展,若需要改变实例的行为,可能需要修改单例类的代码。
- 线程安全问题:在多线程环境下,单例的实现需要特别注意线程安全,若处理不当可能导致数据不一致。
系统为我们提供的单例类:
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不仅有更小的开销,更好的性能,同时还不需要手动管理锁。
代理
协议是多个类(或者对象)之间协商的一个公共接口,提供了一系列方法的声明给类们使用;代理是协议的一个典型应用机制。代理模式的核心思想就是通过代理接口分离使用者和服务提供者,降低了模块之间的耦合度。
协议的编写规范:
一般情况下, 当前协议属于谁, 我们就将协议定义到谁的头文件中
协议的名称一般以它属于的那个类的类名开头, 后面跟上protocol或者delegate
协议中的方法名称一般以协议名称protocol之前的作为开头
一般情况下协议中的方法会将触发该协议的对象传递出去
一般情况下一个类中的代理属于的名称叫做 delegate
当某一个类要成为另外一个类的代理的时候, 一般情况下在.h中用@protocol 协议名称;告诉当前类 这是一个协议.在.m中用#import真正的导入一个协议的声明
注意:
- 协议不能声明成员变量,不能写实现
- 只要父类遵守了某个协议,那么子类也遵守
- 协议可以遵守协议,一个协议遵守了另一个协议,就可以拥有另一份协议中的方法声明
协议中有2个关键字可以控制方法是否要实现(默认是@required,在大多数情况下,用途在于程序员之间的交流)
- @required:这个方法必须要实现(若不实现,编译器会发出警告)
- @optional:这个方法不一定要实现
代理模式的原理
在iOS中代理的本质就是代理对象内存的传递和操作,我们在委托类设置代理对象后,实际上只是用一个id类型的指针将代理对象进行了一个弱引用。委托方让代理方执行操作,实际上是在委托类中向这个id类型指针指向的对象发送消息,而这个id类型指针指向的对象,就是代理对象。
组成部分:
- 协议(Protocol) - 定义了代理需要实现的方法
- 委托方(Delegator) - 持有代理对象并在适当时机调用代理方法
- 代理方(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];
//将委托对象(代理)设置为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
@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更加的方便。
三种工厂模式
通过给定参数来返回对应的实例,完全对用户隐藏其实现的原理。
优势:易于替换,面向抽象编程
依赖倒置原则