设计模式是有用的抽象化工具,用于解决工程和建筑等领域的设计问题。出于同样的目的,软件开发领域借用了这一概念,设计模式是一个对象或类的设计模板,用于解决特定领域经常发生的问题。本文简单介绍了几种在iOS中常用的设计模式:
-
代理模式
为其他对象提供一种代理以控制对这个对象的访问。代理模式的思想是使用一个基本跟实体对象行为相同的代理。在iOS中经常使用代理来进行解耦。例如:
使用代理把View层的事件传递到Controller中
把tableView的delegate和dataSource实现到一个单独的Model或者ViewModel中
代理的使用可以简单的概括为两个三步走,即创建代理的三步走和使用代理的三步走
创建代理:
- 创建代理协议
- 声明代理方法
- 声明代理属性
#import <UIKit/UIKit.h>
@protocol TestDelegate <NSObject>
@optional
-(void)testDelegateWithName:(NSString *)name;
@end
@interface DelegateViewController : UIViewController
@property(weak, nonatomic)id <TestDelegate>delegate;
@end
#import "DelegateViewController.h"
@interface DelegateViewController ()
@end
@implementation DelegateViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
if ([self.delegate respondsToSelector:@selector(testDelegateWithName:)]) {
[self.delegate testDelegateWithName:@"testDelegate"];
}
}
@end
使用代理:
- 遵守代理协议
- 设置代理对象
- 实现代理方法
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
#import "ViewController.h"
#import "DelegateViewController.h"
@interface ViewController ()<TestDelegate>
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
DelegateViewController *delegateVC = [[DelegateViewController alloc]init];
delegateVC.delegate = self;
[self.navigationController pushViewController:delegateVC animated:YES];
}
-(void)testDelegateWithName:(NSString *)name
{
NSLog(@"%@",name);
}
@end
-
单例模式
1.单例模式的作用
确保一个类只有一个实例,而且自行实例化并向系统提供整个实例,这个类被称为单例类,它提供了全局访问的方法。
2.单例模式的使用场景
整个应用程序中,共享一份资源(这个资源只需要创建并初始化一次),一般用于工具类。例如网络请求、支付、以及系统的通知等
3.单例模式等优缺点
优点:
单例模式可以保证系统中一个类只有一个实例并且该实例易于被外界访问,从而方便对实例个数的控制并节约系统资源。如果希望系统中某个类的对象只能存在一个,单例模式是最好的解决办法。单例模式因为类控制了实例化过程,所以类可以更加灵活的修改实例化过程。
缺点:
单例对象一旦建立,对象指针是保存在静态区的,单例对象在堆中分配内存空间,会在应用程序终止后才会释放。单例类无法继承,因此很难扩展。单例类不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景中发生变化,单例就会引起数据错误,不能保存彼此状态。
注意:我们在使用单例类之前,一定要考虑好单例类是否合适以及类的扩展,避免盲目使用单例。
#import <Foundation/Foundation.h>
@interface SingleInstance : NSObject
+(instancetype) shareInstance;
-(void)test;
@end
#import "SingleInstance.h"
static SingleInstance *_instance;
@interface SingleInstance ()<NSCopying,NSMutableCopying>
@end
@implementation SingleInstance
+(instancetype)shareInstance
{
return [[self alloc]init];
}
+(instancetype)allocWithZone:(struct _NSZone *)zone
{
// @synchronized (self) {
// if (_instance == nil) {
// // 为了防止多线程同时访问对象,造成多次内存分配空间,所以要加上线程锁
// _instance = [super allocWithZone: zone];
// }
// return _instance;
// }
static dispatch_once_t onceToken ;
dispatch_once(&onceToken, ^{
if (_instance == nil) {
_instance = [super allocWithZone: zone];
}
});
return _instance;
}
-(id)copyWithZone:(NSZone *)zone
{
return _instance;
}
-(id)mutableCopyWithZone:(NSZone *)zone
{
return _instance;
}
-(void)test
{
NSLog(@"单例测试");
}
@end
-
观察者模式(通知、KVO)
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象。这个主体对象发生变化时,会通知所有观察者对象,是他们能够自动更新。简而言之,就说是A和B,A注册为B的观察者,当B发生变化时通知A,A根据发生的变化,进行处理。
1.通知机制
// 发送通知
NSDictionary *dataDic = [NSDictionary dictionaryWithObject:@"通知值" forKey:@"username"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"NotificationTest"object:nil userInfo:dataDic];
//注册通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registerCompletion:) name:@"NotificationTest" object:nil];
// 接收到通知后的事件处理
-(void)registerCompletion:(NSNotification *)notification{
NSDictionary *theData = notification.userInfo;
NSString *username = [theData objectForKey:@"username"];
NSLog(@"username = %@",username);
}
// 移除通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"NotificationTest" object:nil];
2.KVO
对象的属性发生变化时,通知会直接发送到观察者对象。比如scrollview的滑动监听,观察一个实例的contentOffset属性.
-(void)addObserverMethod{
//添加监听者
[self.tabelview addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
}
//监听属性变化时的回调
-(void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
context:(void *)context
{
NSLog(@"被观察的属性:%@\n旧值:%@\n新值:%@ ",keyPath,(NSString *)change[NSKeyValueChangeOldKey], (NSString *)change[NSKeyValueChangeNewKey]);
}
//取消监听
-(void)dealloc{
[self.tabelview removeObserver:self forKeyPath:@"contentOffset"];
}
-
适配器模式
适配器模式:将一个类的接口转换成客户端希望的另一个接口。适配器模式使用原来由于接口不兼容而不能一起工作的那些类可以一起工作。基本上有两种实现适配器的方式:类适配器和对象适配器。
简单的讲,适配器模式的作用就是封装,接收数据的时候,数据通过中间的适配器处理后再传给控件,主要是自定义控件使用。
#import <Foundation/Foundation.h>
// 被适配者
@interface GLAdaptee : NSObject
@property (copy, nonatomic)NSString *name;
-(void)sayName;
@end
#import "GLAdaptee.h"
@implementation GLAdaptee
-(instancetype)init
{
if (self = [super init]) {
_name = @"Adaptee target";
}
return self;
}
-(void)sayName
{
NSLog(@"%@",self.name);
}
@end
#import <Foundation/Foundation.h>
//Target 目标代理协议
@protocol GLTarget <NSObject>
-(void)speakName;
@end
#import <Foundation/Foundation.h>
#import "GLAdaptee.h"
#import "GLTarget.h"
//Adapter 适配器
@interface GLAdapter : NSObject<GLTarget>
@property (strong, nonatomic) GLAdaptee *adaptee;
@end
#import "GLAdapter.h"
@implementation GLAdapter
-(void)speakName
{
[self.adaptee sayName];
}
@end
// 适配器使用
GLAdaptee *adaptee = [[GLAdaptee alloc]init];
GLAdapter *adapter = [[GLAdapter alloc]init];
adapter.adaptee = adaptee;
id<GLTarget> target = adapter;
[target speakName];
-
策略模式
策略模式定义了一系列的算法,并将其封装起来,是他们之间还可以相互替换,此模式让算法的变化不会影响到使用算法的客户。策略模式让算法独立于使用它的客户而独立变化。
策略模式中的一个关键角色是策略类。它为所有支持的相关算法声明了一个共同接口。
何时使用策略模式:
1.一个类在其操作中使用多个条件语句来定义许多行为。我们可以把相关的条件分支移到他们的策略类中。
2.需要算法的各种变体。
3.需要把复杂的、与算法相关的数据结构暴露给客户端。
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
/// 验证策略的基类
@interface InputValidator : NSObject
/**
策略的输入
@param input 当前的textfield
@return 如果为YES,表示测试通过,如果为NO,表示测试不通过
*/
- (BOOL)validateInput:(UITextField *)input;
/// 当validateInput为NO的时候,我们来读取errorMessage
@property (nonatomic, copy) NSString *errorMessage;
@end
#import "InputValidator.h"
@implementation InputValidator
-(BOOL)validateInput:(UITextField *)input
{
self.errorMessage = @"无子类实现";
return NO;
}
@end
#import "InputValidator.h"
//创建 一个手机验证方法类PhoneNumberValidator,继承于验证策略的基类(InputValidator)
@interface PhoneNumberValidator : InputValidator
//然后重写输入验证的验证方法
-(BOOL)validateInput:(UITextField *)input;
@end
#import "PhoneNumberValidator.h"
@implementation PhoneNumberValidator
- (BOOL)validateInput:(UITextField *)input
{
if (input.text.length <= 0) {
self.errorMessage = @"手机号没有输入";
} else {
if (input.text.length == 11 ) {
self.errorMessage = @"";
} else {
self.errorMessage = @"请输入正确的手机号码";
}
}
return self.errorMessage.length == 0 ? YES : NO;
}
@end
#import "InputValidator.h"
//创建 一个邮箱验证方法类EmailValidator,继承于验证策略的基类(InputValidator)
@interface EmailValidator : InputValidator
//然后重写输入验证的验证方法
-(BOOL)validateInput:(UITextField *)input;
@end
#import "EmailValidator.h"
@implementation EmailValidator
- (BOOL)validateInput:(UITextField *)input
{
if (input.text.length <= 0) {
self.errorMessage = @"邮箱没有输入";
}
else{
self.errorMessage = @"";
}
return self.errorMessage.length == 0 ? YES : NO;
}
@end
#import <UIKit/UIKit.h>
#import "InputValidator.h"
@interface CustomField : UITextField
//抽象的策略
@property (nonatomic, strong) InputValidator *validator;
/**
验证输入合法性
@return 是否合法,不合法,读取InputValidator当中的errorMessage
*/
-(BOOL)validate;
@end
#import "CustomField.h"
@implementation CustomField
- (BOOL)validate {
return [self.validator validateInput:self];
}
@end
// 使用
self.emailField = [[CustomField alloc] initWithFrame:CGRectMake(30, 180, 300, 30)];
self.emailField.placeholder = @"请输入邮箱";
self.emailField.delegate = self;
self.emailField.validator = [EmailValidator new];
[self.view addSubview:self.emailField];
self.phoneNumberField = [[CustomField alloc] initWithFrame:CGRectMake(30, 180 + 40, 300, 30)];
self.phoneNumberField.placeholder = @"请输入电话号码";
self.phoneNumberField.delegate = self;
self.phoneNumberField.validator = [PhoneNumberValidator new];
[self.view addSubview:self.phoneNumberField];
#pragma mark - 文本框代理
- (void)textFieldDidEndEditing:(UITextField *)textField {
CustomField *customField = (CustomField *)textField;
if ([customField validate] == NO) {
NSLog(@"%@",customField.validator.errorMessage);
}
}