IOS开发常用设计模式

说起设计模式,感觉自己把握不了笔头,所以单拿出iOS开发中的几种常用设计模式谈一下。

单例模式(Singleton)

概念:整个应用或系统只能有该类的一个实例

在iOS开发我们经常碰到只需要某类一个实例的情况,最常见的莫过于对硬件参数的访问类,比如UIAccelerometer.这个类可以帮助我们获得硬件在各个方向轴上的加速度,但是我们仅仅需要它的一个实例就够了,再多,只会浪费内存。

所以苹果提供了一个UIAccelerometer的实例化方法+sharedAccelerometer,从名字上我们也能看出此方法让整个应用 共享一个UIAccelerometer实例(PS:iOS 的开放中,我们往往能从方法名中就了解这个方法的作用),它内部的如何实现我们暂且不谈,先来看看还有哪些类同样使用了单例模式。

● UIApplication类提供了 +sharedAPplication方法创建和获取UIApplication单例

● NSBundle类提供了 +mainBunle方法获取NSBundle单例

● NSFileManager类提供了 +defaultManager方法创建和获得NSFileManager单例。(PS:有些时候我们得放弃使用单例模式,使用-init方法去实现一个新的实例,比如使用委托时)

● NSNotificationCenter提供了 +defaultCenter方法创建和获取NSNotificationCenter单例(PS:该类还遵循了另一个重要的设计模式:观察者模式)

● NSUserDefaults类提供了 +defaultUserDefaults方法去创建和获取NSUserDefaults单例

等等,苹果的SDK中大量的遵循此设计模式,那么它的内部是如何实现的呢?

首先给大家介绍一下GCD技术,是苹果针对于多核CPU的多任务解决方案。你不需要了解更多,只需要知道是一组基于C语言开发的API(详细内容可 以看一下唐巧前辈的这篇博文:http://blog.devtang.com/blog/2012/02/22/use-gcd/ )。GCD提供了一个dispatch_once函数,这个函数的作用就是保证block(代码块:暂时理解为一个跟函数相近的东西,具体可以参照这篇文 章:http://blog.csdn.net/enuola/article/details/8674063 )里的语句在整个应用的生命周期里只执行一次。

OK,接下来就给出一个使用了单例模式新建和获取实例的类模版,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//Singleton.h
@interface Singleton : NSObject
+ (Singleton *)sharedSingleton; <1>
@end
  
/***************************************************************/
  
//Singleton.m
#import "Singleton.h"
@implementation Singleton   
static Singleton *sharedSingleton = nil;<2>
  
+ (Singleton *)sharedSingleton{
    static dispatch_once_t once;<3>
    dispatch_once(&once,^{
        sharedSingleton = [[self alloc] init];<4>
        //dosometing
    });
    return sharedSingleton;<5>
}

上述代码中有5小步,解释如下:

1. 声明一个可以新建和获取单个实例对象的方法

2. 声明一个static类型的类变量

3. 声明一个只执行一次的任务

4. 调用dispatch_once执行该任务指定的代码块,在该代码块中实例化上文声明的类变量

5. 返回在整个应用的生命周期中只会被实例化一次的变量

OK,这就是iOS开发中单例模式的机制,下面我们就看看如何在实际开发中使用此模式?(PS:为了尽可能的突出核心内容,我们会对设计中的其他模式或内容一笔带过)

假如我们需要在iOS应用中实现分层的架构设计,即我们需要把数据的持久层,展示层,和逻辑层分开。为了突出重点,我们直接把目光转到持久层,而根 据MVC的设计模式,我们又可以把持久层细分为DAO层(放置访问数据对象的四类方法)和Domain层(各种实体类,比如学生),这样就可以使用DAO 层中的方法,配合实体类Domain层对数据进行清晰的增删改查。那么我们如何设计呢?

从使用者的角度看,我们期望获得DAO层的类实例,然后调用它的增删改查四大方法。可是这个类实例,我们似乎只需要一个就足够了,再多的话不利于管理且浪费内存。OK,我们可以使用单例模式了,代码如下:

.h文件:

1
2
3
4
5
6
7
8
9
10
11
//StudentDAO.h
@interface StudentDAO:NSObject
@property (nonatomic,strong) NSMutaleArray *StudentsInfo;
  
+ (StudentDAO *)sharedStudentDAO;
  
-(int) create:(Student*)student;
-(intremove:(Student*)student;
-(int) modify:(Student*)student;
-(NSMutaleArray) findAll;
@end

.m文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//StudentDAO.m
#import "StudentDAO.h"
#import "Student.h"
@implementation StudentDAO
  
static StudentDAO *studentDao = nil;
+ (StudentDAO)sharedStudentDAO{
    static dispatch_once_t once;
    dispatch_once(&once,^{
        Student  *student1 = [[Student alloc]init];
        student1.name = "MexiQQ";
        student1.studentNum = "201200301101";
  
        Student  *student2 = [[Student alloc]init];
        student2.name = "Ricardo_LI";
        student2.studentNum = "201200301102";
  
        studentDao = [[self alloc] init];
        studentDao._StudentsInfo = [[NSMutaleArray alloc]init];
        [studentDao._StudentsInfo addObject:student1];
        [studentDao._StudentsInfo addObject:student2];
    });
    return studentDao;
}   
//插入的方法
-(int)create:(Student*)stu{
    [self._StudentsInfo addObject:stu];
    return 0;
}   
//删除的方法
-(int)remove:(Student*)stu{
    for(Student* s in self._StudentsInfo){
        if([stu.studentNum isEqual:s.studentNum]){
            [self._StudentsInfo removeObject:s]
            break;
        }
    }
}
-(int)modify...... //省略不写
-(NSMutaleArray)findAll...... //省略不写

上述例子不难理解,其中用到的Student类我这里就不给出了,只是一个含有姓名和学号属性的实体类。

观察者模式

概念:一个对象状态改变,通知正在对他进行观察的对象,这些对象根据各自要求做出相应的改变

图例:

如图所示:操作对象向被观察者对象投送消息,使得被观察者的状态得以改变,在此之前已经有观察者向被观察对象注册,订阅它的广播,现在被观察对象将自己状态发生改变的消息广播出来,观察者接收到消息各自做出应变。

OK,我们先来看看在苹果的Cocoa Touch框架中有谁使用了观察者模式:

通知(notification)机制

原理图如下:

如图所示,在通知机制中对某个通知感兴趣的所有对象都可以成为接受者。首先,这些对象需要向通知中心(NSNotificationCenter) 发出addObserver:selector:name:object:消息进行注册,在投送对象投送通知送给通知中心时,通知中心就会把通知广播给注 册过的接受者。所有的接受者不知道通知是谁投送的,不去关心它的细节。投送对象和接受者是一对多的关系。接受者如果对通知不再关注,会给通知中心发送 removeObserver:name:Object:消息解除注册,以后不再接受通知。

(ps:这段话内容摘抄自关东升先生的文章)

OK,我们试着去使用一下通知机制:

新建一个Single view Project,对项目中的文件做以下修改:

AppDelegate.m

1
2
3
- (void)applicationDidEnterBackground:(UIApplication *)application {
    [[NSNotificationCenter defaultCenter]postNotificationName:@"APPTerminate" object:self];
}

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//
//  ViewController.m
//  TestNotification
//
//  Created by liwenqian on 14-10-18.
//  Copyright (c) 2014年 liwenqian. All rights reserved.
//
  
#import "ViewController.h"
  
@interface ViewController ()
  
@end
  
@implementation ViewController
  
- (void)viewDidLoad {
    [super viewDidLoad];
    //注意此处的selector有参数,要加冒号
    [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(doSomething:) name:@"APPTerminate" object:nil];
    // Do any additional setup after loading the view, typically from a nib.
}
  
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    [[NSNotificationCenter defaultCenter]removeObserver:self];
    // Dispose of any resources that can be recreated.
}
  
#pragma mark -处理通知
-(void)doSomething:(NSNotification*)notification{
    NSLog(@"收到通知");
}
  
@end

如上所示,对模版项目的两个文件的方法或整个文件做出修改,Command+R运行你的APP,再按下Home键(Command+H),会发现打印出一行收到通知的文字,如下:

在APP退到后台时,发出广播,而viewController因为时观察者,收到广播,执行doSomething方法,打印出收到广播的文字。

KVO(Key-Value-Observing)机制

原理图如下:

如图所示:

该机制下观察者的注册是在被观察者的内部进行的,不同于通知机制(由观察者自己注册),需要被观察者和观察者同时实现一个协 议:NSKeyValueObserving,被观察者通过addObserver:forKeypath:options:context方法注册观察 者,以及要被观察的属性。

新建一个single view project,同时新建一个继承自NSObject的TestWatche类:文件结构如下图:

对文件进行如下修改:

AppDelegate.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//
//  AppDelegate.h
//  TestNotification
//
//  Created by liwenqian on 14-10-18.
//  Copyright (c) 2014年 liwenqian. All rights reserved.
//
  
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "TestWatche.h"
  
@interface AppDelegate : UIResponder <UIApplicationDelegate>
  
@property (strong, nonatomic) UIWindow *window;
  
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
@property (strong,nonatomic) NSString *state;
@property (strong,nonatomic) TestWatche *watcher;
  
  
- (void)saveContext;
- (NSURL *)applicationDocumentsDirectory;
  
  
@end

AppDelegate.m 对如下方法做出修改

1
2
3
4
5
6
7
8
9
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {    // Override point for customization after application launch.
  
    self.watcher = [TestWatche alloc];
  
    [self addObserver:self.watcher forKeyPath:@"state" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:@"pass content"];    self.state = @"launch";    return YES;
}
  
- (void)applicationDidEnterBackground:(UIApplication *)application {    self.state = @"backgroud";
}

TestWatche.m(由于继承自NSObject,而NSObject已实现了NSKeyValueObserving协议,所以不需要做声明)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//
//  TestWatche.m
//  TestNotification
//
//  Created by liwenqian on 14-10-18.
//  Copyright (c) 2014年 liwenqian. All rights reserved.
//
  
#import "TestWatche.h"
  
@implementation TestWatche
  
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    NSLog(@"state change:%@",change);
}
@end

OK,Command+B Command+R Command+H看看你的应用输出了什么,如果你的操作无误的话,会显示如下内容:

委托模式

个人认为委托模式大多数人解释的复杂了,其实就像是java中的接口,类可以实现或不实现协议(接口)中的方法。通过此种方式,达到最大的解耦目的,方便项目的扩展。不过你需要设置应用的委托对象,以确定协议中的方法为谁服务。

拿最常用的UITableViewDelegate UITableViewDataSource来举例:

实现一个页面有一个UItableView,UItableView的每一栏(cell)的数据由我们指定,那么我们该如何做呢?苹果也自然想到了 这一点,于是定义了一个接口,这个接口有许多的方法,只需要我们把要服务的对象传进去,就可以使用这些方法了,这个接口就是委托和协议。而 UITableViewDelegate 和 UITableViewDataSource 就是专为UITableView而写的委托和协议。用法如下:

ViewController.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  ViewController.h
//  RecipeBookMe
//
//  Created by liwenqian on 14-9-10.
//  Copyright (c) 2014年 liwenqian. All rights reserved.
//
  
#import <UIKit/UIKit.h>
  
@interface ViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
  
@property (nonatomic, strong) IBOutlet UITableView *mytableView;
  
@end

ViewController.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//
//  ViewController.m
//  RecipeBookMe
//
//  Created by liwenqian on 14-9-10.
//  Copyright (c) 2014年 liwenqian. All rights reserved.
//
  
#import "ViewController.h"
#import "DetailViewController.h"
#import "Recipe.h"
#import "RecipeTableCellTableViewCell.h"
  
@interface ViewController ()
  
@end
  
@implementation ViewController{
     NSArray *recipes;      
}
  
@synthesize mytableView;
  
- (void)viewDidLoad {
    [super viewDidLoad];
  
    // Initialize the recipes array
    Recipe *recipe1 = [Recipe new];
  
    recipe1.name = @"Egg Benedict";
    recipe1.prepTime = @"30 min";
    recipe1.image = @"egg_benedict.jpg";
    recipe1.ingredients = [NSArray arrayWithObjects:@"2 fresh English muffins", @"4 eggs", @"4 rashers of back bacon", @"2 egg yolks", @"1 tbsp of lemon juice", @"125 g of butter", @"salt and pepper", nil];
  
    Recipe *recipe2 = [Recipe new];
    recipe2.name = @"Mushroom Risotto";
    recipe2.prepTime = @"30 min";
    recipe2.image = @"mushroom_risotto.jpg";
    recipe2.ingredients = [NSArray arrayWithObjects:@"1 tbsp dried porcini mushrooms", @"2 tbsp olive oil", @"1 onion, chopped", @"2 garlic cloves", @"350g/12oz arborio rice", @"1.2 litres/2 pints hot vegetable stock", @"salt and pepper", @"25g/1oz butter", nil]; 
  
    recipes = [NSArray arrayWithObjects:recipe1, recipe2, nil];
  
}
  
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
  
/*--------------------------------------------------------------------*/
//定义UITableview的栏目数量
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
     return [recipes count];
}
  
//定义UITableviewCell的显示内容
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CoustomerTableIdentifier = @"RecipeTableCellTableViewCell";
  
    RecipeTableCellTableViewCell *cell =(RecipeTableCellTableViewCell *) [tableView dequeueReusableCellWithIdentifier:CoustomerTableIdentifier];
  
    if (cell == nil) {
       cell = [[RecipeTableCellTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CoustomerTableIdentifier];
    }
  
    recipe = [recipes objectAtIndex:indexPath.row];
  
    cell.nameLabel.text =  recipe.name;
    cell.prepTimeLabel.text= recipe.prepTime;
    cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
  
    return cell;
}
  
//点击每一栏执行跳转时的处理
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
  
        NSIndexPath *indexPath = nil;
        Recipe *recipe = nil;
  
        indexPath = [self.mytableView indexPathForSelectedRow];
        recipe = [recipes objectAtIndex:indexPath.row];
  
        DetailViewController *destViewController = segue.destinationViewController;
        destViewController.recipe = [recipes objectAtIndex:indexPath.row];
    }
}
  
//定义cell的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return 71;
}
/*--------------------------------------------------------------------*/
@end

如上所示,两条/------/注释间的方法全部来自于委托和协议。利用委托和协议,你可以把主要精力放到逻辑业务上,将数据绑定和事件处理交给委托和协议去完成。

IOS开发常用设计模式的更多相关文章

  1. 开发必看 | iOS开发常用设计模式!

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~(一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现.优势:解耦合敏捷原则: ...

  2. iOS开发-常用第三方开源框架介绍

    iOS开发-常用第三方开源框架介绍 图像: 1.图片浏览控件MWPhotoBrowser        实现了一个照片浏览器类似 iOS 自带的相册应用,可显示来自手机的图片或者是网络图片,可自动从网 ...

  3. iOS开发--常用技巧 (MJRefresh详解)

         iOS开发--常用技巧 (MJRefresh详解) https://github.com/CoderMJLee/MJRefresh 下拉刷新01-默认 self.tableView.head ...

  4. IOS开发--常用工具类收集整理(Objective-C)(持续更新)

    前言:整理和收集了IOS项目开发常用的工具类,最后也给出了源码下载链接. 这些可复用的工具,一定会给你实际项目开发工作锦上添花,会给你带来大大的工作效率. 重复造轮子的事情,除却自我多练习编码之外,就 ...

  5. iOS开发的设计模式

    ios开发学习中,经常弄不清楚ios的开发模式,今天我们就来进行简单的总结和探讨~ (一)代理模式 应用场景:当一个类的某些功能需要由别的类来实现,但是又不确定具体会是哪个类实现. 优势:解耦合 敏捷 ...

  6. iOS开发常用国外网站清单

    工欲善其事必先利其器,最近发现临时查找一些东西容易浪费时间,花了点时间整理一下常用的网站,方便以后备用. 国内的code4app,ui4app,cocoachina,oschina,csdn就不说了, ...

  7. iOS开发常用第三方库

    UI 动画 网络相关 Model 其他 数据库 缓存处理 PDF 图像浏览及处理 摄像照相视频音频处理 响应式框架 消息相关 版本新API的Demo 代码安全与密码 测试及调试 AppleWatch ...

  8. iOS开发-常用第三方开源框架介绍(你了解的ios只是冰山一角)--(转)

    图像: 1.图片浏览控件MWPhotoBrowser 实现了一个照片浏览器类似 iOS 自带的相册应用,可显示来自手机的图片或者是网络图片,可自动从网络下载图片并进行缓存.可对图片进行缩放等操作. 下 ...

  9. iOS 开发常用的调试工具

    前言 最近博主临近毕业季,为了完美的写一篇毕业论文,真是:“锄禾日当午,汗滴禾下土”<—— 这句诗跟毕业我写毕业论文没任何一毛钱关系,我就是突然想吟湿了.不过博主作为网络工程专业的好青年,曾经的 ...

随机推荐

  1. 解决mysql无法插入中文数据及插入后显示乱码的问题

    (1)废话不多说就是使用mysql数据库的时候无法输入中文,可以输入中文后显示的又是乱码!! (2开始解决问题: 第一步:找到安装mysql的目录找到 my.ini 文件: 第二步:使用记事本打开my ...

  2. Linux环境下stl库使用(vector)

    step1: #include <iostream> #include <vector> #include <string> using namespace std ...

  3. C# XML操作

    一.简单介绍 using System.Xml; //初始化一个xml实例 XmlDocument xml=new XmlDocument(); //导入指定xml文件 xml.Load(path); ...

  4. linux ssh key配置方法

    转自:http://blog.csdn.net/zzk197/article/details/7915307 一:简洁的配置文件[root@cisco ~]# vi /etc/ssh/sshd_con ...

  5. HR外包系统 - 工资计算-几种常见账单计算规则

    01-正常工资计税 (包括同一月多地计税方式) 02-年终奖计税 (包括可分批发放,但计税总额不变)  按工资 除以月份,看落在那个计税区间,获取税率和扣除数,再用总额*税率-扣除数,要考虑当月工资如 ...

  6. error C2039: “bind2nd”: 不是“std”的成员

    VS2012 出现如下错误: error C2039: "bind2nd": 不是"std"的成员     头文件中加上 #include <functi ...

  7. Waiting Processed Cancelable ShowDialog (Release 2)

    namespace Test { using System; using System.Windows.Forms; static class Program { /// <summary> ...

  8. Codeforces Round #198 (Div. 1) D. Iahub and Xors 二维树状数组*

    D. Iahub and Xors   Iahub does not like background stories, so he'll tell you exactly what this prob ...

  9. HDU 5876 Sparse Graph BFS 最短路

    Sparse Graph Problem Description   In graph theory, the complement of a graph G is a graph H on the ...

  10. POJ 3241 Object Clustering 曼哈顿最小生成树

    Object Clustering   Description We have N (N ≤ 10000) objects, and wish to classify them into severa ...