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都实现了<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展示)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,121评论 29 470
  • 1.项目经验 2.基础问题 3.指南认识 4.解决思路 ios开发三大块: 1.Oc基础 2.CocoaTouch...
    阳光的大男孩儿阅读 4,969评论 0 13
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,857评论 6 13
  • 设计模式 1.delegate和notification什么区别,什么情况使用? 2.描述一下KVO和KVC。 K...
    丶逐渐阅读 1,950评论 3 2