什么是设计模式
设计模式是为特定场景下的问题定制的解决方案,设计模式分三种类型:创建型,结构型,行为型.这里只列举iOS常用到的几种:
**创建型 : **单例 , 工厂
**结构型 : **代理 , MVC
**行为型 : **观察者 , 策略
单例模式
单例类 : 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
优点:
1-节省内存
每次获取实例时会先进行判断,实例存在则返回,否则创建实例,如果一直不用,则不会创建实例,从而节省了内内存空间
2-因为单例类控制实例化过程,所以可以更灵活的修改实例化过程
缺点:
1-运行费时间
上面的判断会浪费一些时间
2-线程安全问题
并发情况下,如果线程A和线程B同时调用某一方法,会创建出两个实例,此时单例模式失效,若想解决线程安全问题,需要加synchronized解决,但会降低访问速度
使用场景:
1-如果创建某一个对象会耗费很多系统资源,此时采取单例模式,因为只需要一个实例,会节省alloc时间
2-若很多模块需要使用同一变量,可以将它放入单例类
3-实际应用:"我的音乐"项目中 "音乐播放器/列表管理/文件管理"均为单例模式
1.定义
+ (instancetype)defaultManager
{
static 单例类名 *instance = nil;
static dispatch_once_t onceToken;
dispatch_once( &onceToken,
^{
instance = [[self alloc] init];
});
return instance;
}
2.使用
[单例类名 defaultManager]调用方法及属性即可
3.注意
假如我们令
_yourList = [CCListManager defaultManager].defaultList;
若你对_yourList执行移除操作
[_yourList removeObjectAtIndex:indexPath.row];
则单例类中的defaultList此行也被移除了
代理模式
当一个类的某些功能(协议)需要由别的类来实现,但是又不确定具体会是哪个类实现,本质是某个类持有了另一个类的指针
协议是约束一个类必须实现某些方法
协议中只能定义方法,不能定义成员变量,属性,@required为必须实现的,@optional为可选择实现的
因为协议这个接口和类并不存在关联关系,所以我们要用到代理,声明一个代理属性,约定实现代理的对象去实现协议方法
说起来有点绕,但理解了就好了....
优点:
1-有利于代码的封装
2-有利于程序的结构化和层次化
3-若是@required的方法,没有实现会编译警告/报错
4-一个类可以定义不同的协议,当然,每个协议得对应不同的delegate
缺点:
1-代码量多,协议定义,delegate属性,本身决定什么时候执行代理,实现代理类的实现
2-释放后,delegate要为nil,否则会是野指针,造成内存泄漏,这也是要用weak来声明delegate的原因
3-只能一对一通信,不过这个好像不算是缺点
备注:
-假如现在有类A,类B,类C
-类C协议为CDelegate,点击类C的按钮时,相应协议,输出一句话
-类A和类B都实现了<CDelegate>方法
-执行的操作是,从类A跳转到类B,从类B跳转至类C,点击类C的按钮,只有类B相应了
使用场景:
tableView的Cell内按钮点击时
音乐播放器当前歌曲的进度
......
实现过程:
举例:类A实现了类B的协议
类B
1.声明协议及协议方法,这是要让别的类,比如“A”实现的
@protocol BDelegate <NSObject>
- (void)passValue: (NSString *)string;
@end
2.声明此协议的代理对象,谁使用了这个代理对象,谁就实现上面的协议,比如“A”
@property(nonatomic,assign)id< BDelegate > delegate;
3.本类决定了,实现我代理对象的类,比如“A”,何时实现我的协议方法,例当我点击我本身的按钮时实现
-(void)onClick
{
[self.delegate passValue:@"少壮不努力,老大徒伤悲啊”];
}
类A
1.声明”B”对象,并实现”B”协议的代理
BViewController *b = [[BViewController alloc] init];
b.delegate = self;
2.实现协议方法,何时实现由”B”控制
- (void)passValue:(NSString *)string
{
NSLog(@"代理传给了我什么%@",string);
}
拿tableView的cell内按钮点击举例
1.在cell中声明协议,代理对象,及何时响应
//声明协议
@class myCell;
@protocol FQDelegate <NSObject>
- (void)clickDelegateButton:(myCell *)cell;
@end
//声明代理对象
@interface myCell : UITableViewCell
//这个名字一般叫delegate,但其实你爱叫什么叫什么
@property (weak, nonatomic) id<FQDelegate> del;
@end
//何时响应
- (IBAction)onClickButton:(id)sender
{
if(self.del && [self.del respondsToSelector:@selector(clickDelegateButton:)])
{
[self.del clickDelegateButton:self];
}
}
2.tableView实现
@interface ViewController ()<UITableViewDataSource,UITableViewDelegate,FQDelegate>
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"myCell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell)
{
cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject];
//点击事件
cell.del = self;
}
//绘制cell内容
//姓名
cell.nameLabel.text = @"亲爱的大倩倩";
//双拼
NSMutableString *mutStr = [NSMutableString string];
[mutStr appendString:@"双"];
[mutStr appendString:@" / "];
[mutStr appendString:@"拼"];
cell.doubleLabel.text = mutStr;
return cell;
}
- (void)clickDelegateButton:(myCell *)cell
{
NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell];
NSLog(@"你当前点击的是第几行%ld",(long)indexPath.row);
}
扩展一:运行时机制
假如我们上面不用代理实现,改成如下依然可以实现:
1.cell中
@property (nonatomic,weak) id delegate;
@property (nonatomic,assign) SEL method;
- (IBAction)onClickButton:(id)sender
{
if ([self.delegate respondsToSelector:self.method])
{
[self.delegate performSelector:self.method withObject:self];
}
}
2.tableView实现
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *identifier = @"myCell";
myCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (!cell)
{
cell = [[[NSBundle mainBundle] loadNibNamed:@"myCell" owner:self options:nil]lastObject];
//点击事件
cell.delegate = self;
cell.method = @selector(onClickMainButton:);
}
return cell;
}
- (void)onClickMainButton:(myCell *) cell
{
NSIndexPath *indexPath = [_mainTableView indexPathForCell:cell];
NSLog(@"你点击的是第几行%ld",(long)indexPath.row);
}
扩展二
1-代理用weak,不用assign的原因
assign和weak均是指针赋值(直接赋值),不改变索引计数,但当被销毁时,
assign:如果使用完毕,不将其置为nil,会产生野指针,操作不当会崩溃
weak:在属性所指的对象遭到摧毁时,属性值也会清空(nil out),不会崩溃
假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b。此时a 和b指向同一块内存,请问当a不再需要这块内存,能否直接释放它?答案是否定的,因为a并不知道b是否还在使用这块内存,如果a释放了,那么b在使用这块内存的时候会引起程序crash掉
2- assigin可以用非 OC 对象,而 weak 必须用于 OC 对象
3- 代理用weak,不用strong的原因,是因为防止循环引用
观察者模式
观察者模式是一种通知变化的模式,分下面两种
Notification
优点:
1-代码量少,实现简单
2-对于一个发出的通知,多个对象能够做出反应,即一对多
缺点:
1-释放通知对象时,需要在通知中心移除注册
2-观察者需要提前知道通知的名字,如果未定义,会不同步
3-编译器不会检查通知是否能被观察者处理(相对比delegate)KVO
优点:
1-简单
2-能够对非我们创建的对象,即内部对象的状态改变做出相应
3-用key path观察属性,因此也可以观察嵌套对象
4-也可以一对多
缺点:
1-观察的属性必须使用NSString定义Delegate,NSNotification,KVO
1-三者均是类和类之间的通信
2-NSNotification和KVO一对多,Delegate一对一
3-NSNotification和KVO不关心接受者的态度,意思就是发出通知后剩下的事就不管了,而delegate会关注结果
NSNotification例子(带参数)
1.声明消息
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(respondNotification:) name:@“FQNotification" object:nil];
2.响应消息
[[NSNotificationCenter defaultCenter] postNotificationName:@“FQNotification”object:@“llama"];
3.实现消息
- (void)respondNotification:(NSNotification*)aNotification
{
NSString *number= [aNotification object];
NSLog(@"接收到的内容是%@",number);
}
KVO会单开专题讲解
MVC模式
Model-View-Controller的缩写,是一种架构模式,讲的是M和V的代码分离,通过数据模型,控制器逻辑,视图展示将应用程序进行逻辑划分。
优点:
1-层次清晰,代码简洁
2-方便测试及改动,易于维护
缺点:
1-增加系统结构和实现的复杂性
2-视图对模型数据低效率访问
这个就不举例子了.
工厂模式
简单工厂模式: 为了向客户提供方便,将分配和初始化合在一个步骤中,返回被创建的对象.说白了,就是对象的封装.
优点:客户端直接使用产品,而不用关心产品的具体实现
缺点:工厂类负责逻辑实现,一旦出现问题,均受影响,而且,每增加一个产品,就要增加一个工厂类
使用场景:多个地方用到某一对象,并且此对象属性一样时
简单工厂模式例子:
每个应用程序都会有自己的主色调,字体颜色及大小等等吧,我们定义每一个Button时,若都写给它设置颜色,字体,字号等太麻烦,可以封装起来,这样声明一个Button只需要执行一句代码就行啦
1.定义一个工具类,里面封装了各种,这里只拿button举例
@interface ToolViewController : UIViewController
#pragma mark - Button
+ (UIButton *)buttonWithFrame:(CGRect)frame buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view;
+ (UIButton *)buttonWithFrame:(CGRect)frame buttonWithTitle:(NSString *)title buttonWithSuperView:(UIView *)view
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = frame;
button.backgroundColor = [UIColor colorWithRed:69/255.0 green:187/255.0 blue:237/255.0 alpha:1];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[button.layer setCornerRadius:5.0];
[view addSubview:button];
return button;
}
2.在你需要的ViewController里,初始化对象
#import "ToolViewController.h"
ToolViewController *nextStepButton = [ToolViewController buttonWithFrame:CGRectMake(10, 200, [UIScreen mainScreen].bounds.size.width - 20, 44) buttonWithTitle:@"下一步" buttonWithSuperView:self.view];
3.一句话就搞定啦,如果设计突然变了颜色,只需要在工具类里改一句话就行啦
类工厂模式:大致可以理解为,通过继承,创建不同的对象
优点:方便替换
缺点:不能算缺点,只是创建的这些类是同一个父类
使用场景:若使用时类会有变动,比如后台若传你不同的数据,需要建不同的类,可以考虑用这个
1.父类为BaseClass,有两个子类ONE和TWO,在父类中
@interface BaseClass : NSObject
+ (instancetype)init;
@end
#import "ONE.h"
#import "TWO.h"
@implementation BaseClass
+ (instancetype)init
{
BaseClass *mainClass = nil;
mainClass = [[ONE alloc] init];
return mainClass;
}
2.在ViewController中声明10个ONE对象(其实应该是在不同的地方声明)
#import "BaseClass.h"
- (void)viewDidLoad
{
[super viewDidLoad];
for (int i = 0; i < 10; i++)
{
BaseClass *myClass = [BaseClass init];
NSLog(@"你建的类为%@",myClass);
}
}
3.此时打印你会发现,你建的是10个ONE对象
2016-07-29 15:05:07.985 基础[1382:93824] 你建的类为<ONE: 0x7fca21f7dc50>
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为<ONE: 0x7fca21e17630>
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为<ONE: 0x7fca21d78ba0>
4.现在我们需要将这10个ONE变成TWO,只需要在BaseClass中改一句话,方便快捷
mainClass = [[TWO alloc] init];
2016-07-29 15:09:46.111 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3f34490>
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3d2af50>
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3cbbbe0>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3d2af50>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为<TWO: 0x7fc8d3e2e600>
再举一个例子,假如后台返回的数据有不同的类型,根据传来的key值不同返回不同的对象
1.在BaseClass中
@interface BaseClass : NSObject
+ (instancetype)initWithDictionary:(NSDictionary *)dictionary;
@end
#import "ONE.h"
#import "TWO.h"
@implementation BaseClass
+ (instancetype)initWithDictionary:(NSDictionary *)dic
{
BaseClass *mainClass = nil;
if ([dic[@"key"] isEqualToString:@"ONE"])
{
mainClass = [[ONE alloc] init];
}
else
{
mainClass = [[TWO alloc] init];
}
return mainClass;
}
@end
2.在ViewController中
#import "BaseClass.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSDictionary *dic1 = [[NSDictionary alloc] initWithObjectsAndKeys:@"ONE",@"key",@"AAA",@"name",nil];
NSDictionary *dic2 = [[NSDictionary alloc] initWithObjectsAndKeys:@"TWO",@"key",@"BBB",@"name",nil];
NSMutableArray *arr = [[NSMutableArray alloc] initWithObjects:dic1,dic2, nil];
for (NSDictionary *dic in arr)
{
BaseClass *myClass = [BaseClass initWithDictionary:dic];
NSLog(@"这个模型是%@",myClass);
}
}
3.结果
2016-07-29 15:13:58.714 基础[1544:107073] 这个模型是<ONE: 0x7fb450e0cda0>
2016-07-29 15:13:58.715 基础[1544:107073] 这个模型是<TWO: 0x7fb450cae900>
策略模式
和工厂模式相比,策略模式是对算法的封装,使算法可以互相替换.
之前看过一个例子,假如你出去旅游,有多种方案选择,可以骑自行车,汽车,火车,飞机,每个策略都可以得到相同的结果,但使用的是不同的资源
有一篇讲策略模式很好的文章,推荐一下
设计模式之策略模式(iOS开发,代码用Objective-C展示)