iOS设计模式篇-设计模式知多少

什么是设计模式
设计模式是为特定场景下的问题定制的解决方案,设计模式分三种类型:创建型,结构型,行为型.这里只列举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都实现了方法
-执行的操作是,从类A跳转到类B,从类B跳转至类C,点击类C的按钮,只有类B相应了

使用场景:
tableView的Cell内按钮点击时
音乐播放器当前歌曲的进度
......

实现过程:
举例:类A实现了类B的协议
类B
1.声明协议及协议方法,这是要让别的类,比如“A”实现的

@protocol BDelegate 
- (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 
- (void)clickDelegateButton:(myCell *)cell;
@end

//声明代理对象
@interface myCell : UITableViewCell
//这个名字一般叫delegate,但其实你爱叫什么叫什么
@property (weak, nonatomic) id del;
@end

//何时响应
- (IBAction)onClickButton:(id)sender
{
        if(self.del && [self.del respondsToSelector:@selector(clickDelegateButton:)])
        {
            [self.del clickDelegateButton:self];
        }
}

2.tableView实现
@interface ViewController ()

- (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);
}
iOS设计模式篇-设计模式知多少_第1张图片

扩展一:运行时机制
假如我们上面不用代理实现,改成如下依然可以实现:

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.一句话就搞定啦,如果设计突然变了颜色,只需要在工具类里改一句话就行啦
iOS设计模式篇-设计模式知多少_第2张图片

类工厂模式:大致可以理解为,通过继承,创建不同的对象
优点:方便替换
缺点:不能算缺点,只是创建的这些类是同一个父类
使用场景:若使用时类会有变动,比如后台若传你不同的数据,需要建不同的类,可以考虑用这个

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] 你建的类为
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.986 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为
2016-07-29 15:05:07.987 基础[1382:93824] 你建的类为

4.现在我们需要将这10个ONE变成TWO,只需要在BaseClass中改一句话,方便快捷
mainClass = [[TWO alloc] init];

2016-07-29 15:09:46.111 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.112 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为
2016-07-29 15:09:46.113 基础[1479:100444] 你建的类为

再举一个例子,假如后台返回的数据有不同的类型,根据传来的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] 这个模型是
2016-07-29 15:13:58.715 基础[1544:107073] 这个模型是

策略模式

和工厂模式相比,策略模式是对算法的封装,使算法可以互相替换.

之前看过一个例子,假如你出去旅游,有多种方案选择,可以骑自行车,汽车,火车,飞机,每个策略都可以得到相同的结果,但使用的是不同的资源

有一篇讲策略模式很好的文章,推荐一下
设计模式之策略模式(iOS开发,代码用Objective-C展示)

你可能感兴趣的:(iOS设计模式篇-设计模式知多少)