iOS 设计模式
很赞的总结
中文版
https://github.com/mobilefeng/BlueLibrary
==========MVC(model, view, controller)模式==========
1. model
Album:专辑数据
Album+TableRepresentation:扩展专辑数据,专门给UITableView展示用⭐️
2. view
AlbumView:专辑图片
HorizontalScroller:自定义的横滚视图 ⭐️
3. controller
ViewController
@interface ViewController () <UITableViewDataSource, UITableViewDelegate, HorizontalScrollerDelegate> { // Model NSArray *allAlbums; NSDictionary *currentAlbumData; NSInteger currentAlbumIndex; // View HorizontalScroller *scroller; UITableView *dataTable; }
- C管理着M和V,在C中有5个属性
= 在viewdidload中将两个view添加到C的self.view中
- C声明实现三个协议
= 将V的委托属性设置为C
= 在C中实现了协议中声明的方法
==========单例(Singleton)模式==========
官方定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
// LibraryAPI.h + (instancetype)sharedInstance; // LibraryAPI.m + (instancetype)sharedInstance { static id _sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedInstance = [[LibraryAPI alloc] init]; }); return _sharedInstance; } // ViewController allAlbums = [[LibraryAPI sharedInstance] getAlbums];
1. 在.h中声明一个类方法,返回一个实例
2. 声明一个静态变量 _sharedInstance 保存类的实例
3. 声明一个静态变量 dispatch_once_t ,它确保初始化器代码只执行一次
4. 使用 GCD 执行初始化LibraryAPI变量的block,这正是单例模式的关键:一旦类已经被初始化,初始化器永远不会再被调用。
5. 调用时直接使用类方法
==========外观(Facade)模式==========
官方定义:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一系统更加容易使用
(比如把用户中心、商品中心、交易中心、检索、营销、客服统一起来,提供BNAPI接口,供APP调用,这就是活生生的外观模式)
外观模式,是将一些复杂的类封装起来提供单一接口
用 PersistencyManager 本地保存专辑数据,用 HTTPClient 处理远程连接
用 LibraryAPI 保存 PersistencyManager 和 HTTPClient 的实例,然后 LibraryAPI 将暴露一个简单的API去访问这些服务
@interface LibraryAPI (){ PersistencyManager *persistencyManager; HTTPClient *httpClient; BOOL isOnline; }
==========装饰器(Decorator)模式==========
官方定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
在不修改原来代码的情况下动态的给对象增加新的行为和职责,它通过一个对象包装被装饰对象的方法来修改类的行为,这种方法可以做为子类化的一种替代方法
Category (类别)
在 Album 类基础上扩展出 Album+TableRepresentation 类
@interface Album (TableRepresentation) // tr is short for TableRepresentation - (NSDictionary*)tr_tableRepresentation; @end
- 创建的方法
1. Command + N
2. 选择 iOS - Source - Objective-C File
3. File Type 选择 Category,Class 填基于的类名,File填扩展的名
- 特点
可以直接使用 Album的属性
==========适配器(Adapter)模式==========
官方定义:将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
在C++中用多重继承实现,但是OC是没有多重继承的,于是OC中是通过协议来实现
1 // ViewController.h 2 @interface ViewController : UIViewController 3 4 // ViewController.m 5 @interface ViewController () <UITableViewDataSource, UITableViewDelegate, HorizontalScrollerDelegate> { 6 // ... 7 HorizontalScroller *scroller; 8 UITableView *dataTable; 9 }
一方面,ViewController 继承了 UIViewController
另一方面,为了能利用类 HorizontalScroller,将其方法封装成协议 HorizontalScrollerDelegate(也就是接口),供 ViewController 使用
Delegation (委托)
创建委托协议
1 @protocol HorizontalScrollerDelegate;
2
3 @interface HorizontalScroller : UIView
4
5 // weak 防止循环引用
6 // id 意味着 delegate 属性可以用任何遵从 HorizontalScrollerDelegate 的类赋值
7 @property (weak) id<HorizontalScrollerDelegate> delegate;
8 - (void)reload;
9
10 @end
11
12 // _____________________________________________________________________________________
13 //
14 @protocol HorizontalScrollerDelegate <NSObject>
15
16 @required
17 - (NSInteger)numberOfViewsForHorizontalScroller:(HorizontalScroller*)scroller;
18
19 - (UIView*)horizontalScroller:(HorizontalScroller*)scroller viewAtIndex:(int)index;
20
21 - (void)horizontalScroller:(HorizontalScroller*)scroller clickedViewAtIndex:(int)index;
22
23 @optional
24 - (NSInteger)initialViewIndexForHorizontalScroller:(HorizontalScroller*)scroller;
25
26 @end
主要分两块:
1. 14-26行是委托协议的声明,这里声明了一个名为 HorizontalScrollerDelegate 的委托协议,包含三个必须实现方法和一个可选实现方法
2. 第7行是给 HorizontalScroller 定义了一个实现 HorizontalScrollerDelegate 的属性 delegate
使用委托协议
1 // 1
2 @interface ViewController () <UITableViewDataSource, UITableViewDelegate, HorizontalScrollerDelegate> {
3 // ...
4 HorizontalScroller *scroller;
5 UITableView *dataTable;
6 }
7
8 // 2
9 - (void)viewDidLoad {
10 [super viewDidLoad];
11
12 // ...
13 scroller.delegate = self;
14 dataTable.delegate = self;
15 // ...
16 }
17
18 // 3
19 // 实现协议中声明的方法
主要分三块:
1. 第2行,在类的interface声明实现XX委托协议,4-5行,通常有实现相应协议对应的属性
2. 13-14行,将熟悉的delegate设置为类本身(self)
3. 19行,实现委托协议中声明的方法
如果一个协议包含的方法太多,可以考虑将其拆分,如:
UITableViewDataSource 和 UITableViewDelegate,前者负责数据源获取,后者负责对数据源进行加工细化
==========观察者(Observer)模式==========
官方定义:又叫发布-订阅(Publish/Subscribe)模式,定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
这是一种松耦合的设计,在MVC中,当需要让模型对象和视图对象在不相互引用的情况下进行通信,就需要观察者模式。
Cocoa中有两种常见的观察者模式:通知(Notification)和KVO(Key-Value Observing)
通知(Notification)
1 // AlbumView.m 2 [[NSNotificationCenter defaultCenter] postNotificationName:@"BLDownloadImageNotification" object:self userInfo:@{@"imageView":coverImage, @"coverUrl":albumCover}]; 3 4 // LibraryAPI.m 5 - (id)init { 6 if (self = [super init]) { 7 // ... 8 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(downloadImage:) name:@"BLDownloadImageNotification" object:nil]; 9 } 10 11 return self; 12 } 13 14 - (void)downloadImage:(NSNotification *)notification { 15 UIImageView *imageView = notification.userInfo[@"imageView"]; 16 NSString *coverUrl = notification.userInfo[@"coverUrl"]; 17 // ... 18 } 19 20 - (void)dealloc { 21 [[NSNotificationCenter defaultCenter] removeObserver:self]; 22 }
1. 第2行,发送名为BLDownloadImageNotification通知,其中userInfo会作为通知的一部分传递过去(15-16行)
2. 第8行,LibraryAPI 注册为 BLDownloadImageNotification 的观察者,当 AlbumView 发送通知时,系统会通知 LibraryAPI,LibraryAPI 调用 downloadImage: 来下载图片
3. 20-22行,观察者在销毁时,必须退订之前订阅的所有通知,否则一个通知发送给一个已经销毁的对象,会crash
KVO(Key-Value Observing)
它提供一种机制,当指定的对象的属性被修改后,则对象就会接受到通知。简单的说就是每次指定的被观察的对象的属性被修改后,KVO就会自动通知相应的观察者了。
1 // AlbumView.m 2 // 注册 3 [coverImage addObserver:self forKeyPath:@"image" options:0 context:nil]; 4 // 实现回调方法 5 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 6 if ([keyPath isEqualToString:@"image"]) { 7 [indicator stopAnimating]; 8 } 9 } 10 // 移除观察 11 - (void)dealloc { 12 [coverImage removeObserver:self forKeyPath:@"image"]; 13 }
1. 注册通知
2. 实现回调方法
3. 移除观察(退订通知)
两点心得:
1. 通知是解决两个类间的消息传递,KVO是解决类内部属性间的消息传递
2. dealloc 中主要就是移除观察(退订通知)
==========观察者(Observer)模式==========