iOS,几种设计模式

时间:2023-12-20 11:18:32

1.单例模式

2.观察者模式

3.委托代理

4.block回调

5.反射机制

6.多代理模式

单例模式

iOS单例模式的2种方式。根据线程安全的实现来区分,一种是使用@synchronized ,另一种是使用GCD的dispatch_once函数。

要实现单例,首先需要一个static的指向类本身的对象,其次需要一个初始化类函数。下面是两种实现的代码。

@synchronized

static InstanceClass *instance;
+ (InstanceClass *)shareInstance{
@synchronized (self){
if (instance == nil) {
instance = [[InstanceClass alloc] init];
}
}
return instance;
}

GCD

static InstanceClass *instance;
+ (InstanceClass *)shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[InstanceClass alloc] init];
});
return instance;
}

示例:

+(MyClass *)shareInstance
{
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [[self alloc]init];
});
return instance;
}

观察者模式

Notification(通知)

//向通知中心添加消息监听名称,和通知方法
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(viewDisappear:) name:@"DismissFaceVerifyController" object:nil]; //通过通知中心调用对应消息名
[[NSNotificationCenter defaultCenter] postNotificationName:@"DismissFaceVerifyController" object:self userInfo:nil];

KVO(键值监听),即Key-Value Observing

监听对象的属性(成员变量)变化,对象可以是自己也可以是其它

//实例监听自己的属性

//ViewController.m文件

//
// ViewController.m
// KVOLearn
//
// Created by Vie on 2017/4/6.
// Copyright © 2017年 Vie. All rights reserved.
// #import "ViewController.h" @interface ViewController ()
@property(strong, nonatomic) NSString *testStr;
@end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; self.testStr=@"Tracer"; //按钮
UIButton *loginBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width/-, , , )];
[loginBtn setTitle:@"改变值" forState:UIControlStateNormal];
[loginBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[loginBtn setBackgroundColor:[UIColor redColor]];
[loginBtn.layer setBorderWidth:0.2f];
[loginBtn.layer setCornerRadius:10.0f];
[loginBtn addTarget:self action:@selector(changeAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:loginBtn]; /*1.注册对象self为被观察者: option中,
forKeyPath为被观察的对象属性
NSKeyValueObservingOptionOld 以字典的形式提供 “初始对象数据”;
NSKeyValueObservingOptionNew 以字典的形式提供 “更新后新的数据”; */
[self addObserver:self forKeyPath:@"testStr" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}
//按钮事件
-(void)changeAction:(UIButton *)sender{
self.testStr=@"Vie";
} /* 只要object的keyPath属性发生变化,就会调用此回调方法*/
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"testStr"]) {
NSLog(@"监听到testStr属性值改变");
}
}
-(void)dealloc{
//移除self中forKeyPath属性观察
[self removeObserver:self forKeyPath:@"testStr" context:nil];
} @end

委托代理

delegate

委托应用场景:一个view把一些业务交给 控制器去实现,然后控制器实现委托改变view

//在ViewController.h中声明协议如下

@protocol ByValueDelegate <NSObject>
-(void) passValue:(NSString *) value;
@end

//在ViewController.h中声明委托变量

@property (nonatomic,weak) id<ByValueDelegate> delegate;//声明一个委托变量 

//在ViewController.m中设置代理

UserinfoView *uiv=[[UserinfoView alloc]init];
//把值委托到UserinfoView.m的实现委托的方法里面
self.delegate=uiv;//设置该对象代理为对象uiv
[self.delegate passValue:@"委托传值跳转"];

//在UserinfoView.h中,引用ViewController的头文件,并添加代理协议如下

#import "ViewController.h" 

@interface UserinfoView : UIViewController<ByValueDelegate>@property (nonatomic,strong) UILabel *showLable;
@property (nonatomic,retain) UserinfoEntity *showUserinfoEntity;
-(void) viewDidLoad;@end

//在UserinfoView.m中实现代理函数

//实现委托方法
-(void) passValue:(NSString *)value{
UIAlertView *alterOk = [[UIAlertView alloc] initWithTitle:@"提示" message:value delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alterOk show];
}

KVC(键值编码),即Key-Value Coding,一个非正式的Protocol,使用字符串(键)访问一个对象实例变量的机制

就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态在访问和修改对象的属性。而不是在编译时确定,这也是iOS开发中的黑魔法之一。

KVC在iOS中的定义

无论是Swift还是Objective-C,KVC的定义都是对NSObject的扩展来实现的(Objective-c中有个显式的NSKeyValueCoding类别名,而Swift没有,也不需要)所以对于所有继承了NSObject在类型,都能使用KVC(一些纯Swift类和结构体是不支持KVC的),下面是KVC最为重要的四个方法
//KVC最为重要的四个方法

- (nullable id)valueForKey:(NSString *)key;                          //直接通过Key来取值
- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值
- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值

//例

//PersonInfoModel.h文件

//
// PersonInfoModel.h
// KVCLearn
// 个人信息
// Created by Vie on 2017/4/6.
// Copyright © 2017年 Vie. All rights reserved.
// #import <Foundation/Foundation.h> @interface PersonInfoModel : NSObject
@property(strong, nonatomic) NSString *name;//姓名
@property(strong, nonatomic) NSString *gender;//性别
@property(assign, nonatomic) NSNumber *age;//年龄
@end

//StudentInfoModel.h文件

//
// StudentInfoModel.h
// KVCLearn
// 学生信息
// Created by Vie on 2017/4/6.
// Copyright © 2017年 Vie. All rights reserved.
// #import <Foundation/Foundation.h>
#import "PersonInfoModel.h"
@interface StudentInfoModel : NSObject
@property(strong, nonatomic)PersonInfoModel *personModel;//个人信息
@property(strong, nonatomic)NSString *studentID;//学号
@property(strong, nonatomic)NSString *departments;//院系
@end

//使用

#import "StudentInfoModel.h"

 StudentInfoModel *studentModel=[[StudentInfoModel alloc] init];
//设值
[studentModel setValue:@"信息技术学院" forKey:@"departments"];
[studentModel setValue:@"Vie" forKeyPath:@"personModel.name"];
//取值
NSLog(@"%@",[studentModel valueForKey:@"departments"]);
NSLog(@"%@",[studentModel valueForKeyPath:@"departments"]);

修改系统控件内部属性(runtime + KVC)

将UIPageControl样式原来样式

iOS,几种设计模式

修改为下面

iOS,几种设计模式

PageControl公开属性没有直接可以修改的,目前可以自定义PageControl,这种方式看起来不简单。另一种方式就是,通过runtime遍历出UIPageControl所有属性(包括私有成员属性,runtime确实很强大)。

 //遍历属性
unsigned int count = ;
Ivar *ivars = class_copyIvarList([UIPageControl class], &count);
for (int i = ; i < count; i++) {
Ivar ivar = ivars[i];
//获取所有私有属性
const char *property = ivar_getName(ivar);
NSLog(@"%@",[[NSString alloc]initWithCString:property encoding:NSUTF8StringEncoding]);
}

//运行结果

-- ::20.756 KVCLearn[:] _lastUserInterfaceIdiom
-- ::20.756 KVCLearn[:] _indicators
-- ::20.757 KVCLearn[:] _currentPage
-- ::20.757 KVCLearn[:] _displayedPage
-- ::20.757 KVCLearn[:] _pageControlFlags
-- ::20.757 KVCLearn[:] _currentPageImage
-- ::20.757 KVCLearn[:] _pageImage
-- ::20.757 KVCLearn[:] _currentPageImages
-- ::20.758 KVCLearn[:] _pageImages
-- ::20.758 KVCLearn[:] _backgroundVisualEffectView
-- ::20.758 KVCLearn[:]_currentPageIndicatorTintColor
-- ::20.758 KVCLearn[:] _pageIndicatorTintColor
-- ::20.758 KVCLearn[:] _legibilitySettings
-- ::20.758 KVCLearn[:] _numberOfPages

//找到了属性后,接着实现

//然后通过KVC设置自定义图片,实现了效果,代码如下:
UIPageControl *pageControl = [[UIPageControl alloc] init];
[pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"];
[pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];

block回调

block中使用修改方法外的属性需要用__block修饰,例如:

__block BOOL whoopsSomethingWrongHappened = true;

//ViewController.m文件
#import "ViewController.h"
#import "CallbackTest.h"
@interface ViewController () @end @implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor whiteColor]];
UIButton *backBtn=[[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width*0.4, self.view.frame.size.height*0.475, self.view.frame.size.width*0.2, self.view.frame.size.height*0.05)];
[backBtn.layer setCornerRadius:5.0];
[backBtn setTitle:@"回调" forState:UIControlStateNormal];
[backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backBtn.layer setBorderWidth:0.5];
[backBtn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:backBtn];
} /**
* @Author Vie, 2015-08-05 13:41:37
* 使用block回调函数
* @param sender sender description
*
* @since <#version number#>
*/
-(void)btnAction:(UIButton *)sender{
CallbackTest *backTest=[[CallbackTest alloc] init];
//第一种回调方式
// [backTest startString:@"回调开始" callBack:^(NSString *backFunstring) {
// 回调时候会执行
// UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"回调提示" message:backFunstring delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
// [alert show];
//
// }]; //第二种回调方式
backTest.backCall=^(NSString *string){
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"回调提示"message:string delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];
};
[backTest blockCall:@"回调开始"];
}
@end
//CallbackTest.h文件
#import <UIKit/UIKit.h>
@interface CallbackTest : NSObject
//定义一个block,返回值为void,参数为NSString
typedef void(^backFunction)(NSString *backFunstring);
@property (nonatomic,copy) backFunction backCall;
-(void)startString:(NSString *)str callBack:(backFunction)backString;
-(void)blockCall:(NSString *)str;//第一一个block属性
@end
//CallbackTest.m文件
#import "CallbackTest.h"

@implementation CallbackTest
//第一种回调方式
-(void)startString:(NSString *)str callBack:(backFunction)backString{
NSMutableString *mutString=[str mutableCopy];
[mutString appendString:@"回调完成"];
//执行block
// backString(mtuString);
//将block回调交给另一个方法执行
[self TestBackCall:mutString callBack:backString];
} //执行block回调的函数
-(void)TestBackCall:(NSMutableString *)mutString callBack:(backFunction)testBack{
testBack(mutString);
} //第二种回调方式
-(void)blockCall:(NSString *)str{
NSMutableString *mutString=[str mutableCopy];
[mutString appendString:@"回调完成"];
_backCall(mutString);
}
@end

反射机制

//使用NSClassFromString,通过字符串获取类
Class class=NSClassFromString(@"UIAlertView");
//或者使用objc_getClass,通过字符串获取类
//Class class=objc_getClass("UIAlertView");
id alert= [[class alloc] initWithTitle:@"" message:@"" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alert show];

多代理模式

  使用NSPointerArray进行多代理可以跟踪内存,它是mutable,数组不会增加这个对象的引用计数

//例

//MoreDelegate.h文件

//
// MoreDelegate.h
// MoreDelegate
// 委托
// Created by Vie on 2017/5/2.
// Copyright © 2017年 Vie. All rights reserved.
//
@protocol MoreDelegate <NSObject> @optional
-(void)testMoreDelegate;//多代理测试 @end

//后面可以结合通知(或者其他方式)使用,到达方法准确调用带参。

//MoreDelegateManager.h文件

//
// MoreDelegateManager.h
// MoreDelegate
// 多代理管理实现类
// Created by Vie on 2017/5/2.
// Copyright © 2017年 Vie. All rights reserved.
// #import <Foundation/Foundation.h>
#import "MoreDelegate.h" @interface MoreDelegateManager : NSObject /**
单例模式 @return self
*/
+(MoreDelegateManager *)shareInstance; /**
注册代理 @param obj 需要实现代理的对象
*/
-(void)registerDelegate:(id)obj; /**
解除代理 @param obj 需要接触代理的对象
*/
-(void)unRegisterDelegate:(id)obj; /**
执行代理方法(后续可以多个方法提供出去,方便传参调用) @param aSelector 类方法选择器
*/
-(void)delegateAction:(SEL)aSelector;
@end

//MoreDelegateManager.m文件

//
// MoreDelegateManager.m
// MoreDelegate
//
// Created by Vie on 2017/5/2.
// Copyright © 2017年 Vie. All rights reserved.
// #import "MoreDelegateManager.h" #import <objc/runtime.h>
#import <objc/message.h> @interface MoreDelegateManager ()<MoreDelegate>
@property(nonatomic,strong) NSPointerArray *delegateArr;//实现委托的数组
@end @implementation MoreDelegateManager
#pragma mark 懒加载
-(NSPointerArray *)delegateArr{
if (!_delegateArr) {
_delegateArr=[NSPointerArray weakObjectsPointerArray];
}
return _delegateArr;
} #pragma mark 对外方法
/**
单例模式 @return self
*/
+(MoreDelegateManager *)shareInstance{
static id instanc;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instanc=[[self alloc] init];
});
return instanc;
}
/**
注册代理 @param obj 需要实现代理的对象
*/
-(void)registerDelegate:(id)obj{
NSLog(@"注册代理类:%@",NSStringFromClass([obj class].class));
[self.delegateArr addPointer:(__bridge void*)obj];
} /**
解除代理 @param obj 需要接触代理的对象
*/
-(void)unRegisterDelegate:(id)obj{
NSLog(@"解除注册代理类:%@",NSStringFromClass([obj class].class));
NSUInteger index=[self indexOfDelegte:obj];
if (index!=NSNotFound) {
[self.delegateArr removePointerAtIndex:index];
}
//去掉数组里面的野指针
[self.delegateArr compact];
} /**
执行代理方法 @param aSelector 类方法选择器
*/
-(void)delegateAction:(SEL)aSelector{
for (id delegate in self.delegateArr) {
if (delegate&&[delegate respondsToSelector:aSelector]) {
// ((void (*) (id, SEL)) objc_msgSend) (delegate, aSelector);
[delegate testMoreDelegate];
}
}
} #pragma mark 内部使用方法 /**
计算Delegate的下标 @param obj 代理委托类
@return 下标
*/
-(NSUInteger)indexOfDelegte:(id)obj{
for (NSUInteger i=; i<self.delegateArr.count; i++) {
if ([_delegateArr pointerAtIndex:i]==(__bridge void*)obj) {
return i;
}
}
return NSNotFound;
}
@end

//使用

//在几个不同的控制器中注册代理
[[MoreDelegateManager shareInstance] registerDelegate:self]; //调用事件
[[MoreDelegateManager shareInstance] delegateAction:@selector(testMoreDelegate)]; //实现委托方法
-(void)testMoreDelegate{
NSLog(@"代理实现类:%@",NSStringFromClass(self.class));
}