iOS架构
Model
定义一个model层的最重要的理由是,它为我们的程序提供了一个表述事实的单一来源,这会让逻辑清晰,行为正确。这样一来,我们的程序便不会被应用框架中的实现细节所支配。
MVC
image.png观察者模式是在 MVC 中维持 model 和 view 分离的关键。这种方式的优点在于,不论变更究竟是源自哪里 (比如, view 事件、后台任务或者网络),我们都可以确信 UI 是和 model 数据同步的。而且,在遇到变更请求时,model 将有机会拒绝或者修改这个请求
image.png
MVC最主要问题是View Controller肥大。View controller 需要负责处理 view 层 (设置 view 属性,展示 view 等),但是它同时也负责 controller 层的任务 (观察 model 以及更新 view),最后,它还要负责 model 层 (获取数据,对其变形或者处理)。结合它在架构中的中心角色,这使得我们很容易在不经意间把所有的职责都赋予 view controller,从而迅速让程序变得难以管理。
MVC难以测试
MVVM-C
MVVM-C
- Model层:请求的原始数据
- View层:视图展示,由ViewController来控制
- ViewModel层:负责业务处理和数据转化
- view controller: 不再拥有内部的 view state,这些状态也被移动到了 view-model 中。在 MVC 中 view controller 的双重角色 (既作为 view 层级的一部分,又负责协调 view 和 model 之间的交互),减少到了单一角色 (view controller 仅仅只是 view 层级的一部分)。
- C(协调器):view controller 也不再拥有内部的 view state,这些状态也被移动到了 view-model 中。在 MVC 中 view controller 的双重角色 (既作为 view 层级的一部分,又负责协调 view 和 model 之间的交互),减少到了单一角色 (view controller 仅仅只是 view 层级的一部分)。
构建
view controller 不再直接为每个 view 获取和准备数据,它会把这项工作交给 view-model。View controller 在创建的时候会一并创建 view-model,并且将每个 view 绑定到 view-model 所暴露出的相应属性上去(严格的MVVM模式,view-model不应该持有view)。
更改 Model
在 MVVM 中,view controller 接收 view 事件的方式和 MVC 中一样 (在 view 和 view controller 之间建立连接的方式也相同)。不过,当一个 view 事件到达时,view controller 不会去改变自身的内部状态、view state、或者是 model。相对地,它立即调用 view-model 上的方法,再由 view-model 改变内部状态或者 model。
变更View
和 MVC 不同,view controller 不监听 model。View-model 将负责观察 model,并将 model 的通知转变为 view controller 可以理解的形式。View controller 订阅 view-model 的变更,这通常通过一个响应式编程框架来完成,但也可以使用任意其他的观察机制。当一个 view-model 事件来到时,由 view controller 去更改 view 层级。
为了实现单向数据流,view-model 总是应该将变更 model 的 view action 发送给 model,并且仅仅在 model 变化实际发生之后再通知相关的观察者。
View State
View state 要么存在于 view 自身之中,要么存在于 view-model 里。和 MVC 不同,view controller 中不存在任何 view state。View-model 中的 view state 的变更,会被 controller 观察到,不过 controller 无法区分 model 的通知和 view state 变更的通知。当使用协调器时,view controller 层级将由协调器进行管理。
MVC+VS
image.png在标准的MVC 中,View state 要么存在于 view 自身之中,要么存在于 view-model 里。和 MVC 不同,view controller 中不存在任何 view state。View-model 中的 view state 的变更,会被 controller 观察到,不过 controller 无法区分 model 的通知和 view state 变更的通知。当使用协调器时,view controller 层级将由协调器进行管理。
因为 view state model 和文档 model 都需要观察,所以相比于典型的 MVC 来说,我们需要多得多的通过通知进行观察的函数。
MVP
Model-View-Presenter (MVP) 是一种在 Android 上很流行的模式,在 iOS 中,也有相应的实现。P持有view和model
a.png
c.png
网络请求处理
集约型API、离散型API
- 集约型:如AFNetworking
- 离散型:离散型API调用是这样的,一个API对应于一个APIManager,然后这个APIManager只需要提供参数就能起飞,API名字、着陆方式都已经集成入APIManager中。
- 如果是针对提供翻页机制的API,APIManager就能简单地提供loadNextPage方法去加载下一页,页码的管理就不用业务方去管理了
- 如果是界面刷新请求这种,而且存在重复请求的情况(下拉刷新时,在请求着陆之前用户不断执行下拉操作),那么这个时候,离散型可以控制后面重复操作API请求不再发送了。
当一个页面的请求正在天上飞的时候,用户等了好久不耐烦了,小手点了个back,然后ViewController被pop被回收。此时请求的着陆点就没了。这是很危险的情况,着陆点要是没了,就很容易crash的。一般来说处理这个情况都是在dealloc的时候取消当前页面所有的请求。如果是集约型的API调用,这个代码就要写到ViewController的dealloc里面,但如果是离散型的API调用,这个代码写到APIManager里面就可以了,然后随着ViewController的回收进程,APIManager也会被跟着回收,这部分代码就得到了调用的机会。这样业务方在使用的时候就可以不必关心着陆点消失的情况了,从而更加关注业务。
继承的时候
这就要求所有子类必须遵守这个protocol,所有针对父类的重载、覆盖也都以这个protocol为准,protocol以外的方法不允许重载、覆盖。而在父类的代码里,可以不必遵守这个protocol,保持了未来维护的灵活性。
BaseAPIManager的init方法里这么写:
// 注意是weak。
@property (nonatomic, weak) id<APIManager> child;
- (instancetype)init
{
self = [super init];
if ([self conformsToProtocol:@protocol(APIManager)]) {
self.child = (id<APIManager>)self;
} else {
// 不遵守这个protocol的就让他crash,防止派生类乱来。
NSAssert(NO, "子类必须要实现APIManager这个protocol。");
}
return self;
}
protocol这么写,把原本要重载的函数都定义在这个protocol里面,就不用在父类里面写空方法了:
@protocol APIManager <NSObject>
@required
- (NSString *)apiMethodName;
...
@end
然后在父类里面如果要使用的话,就这么写:
[self requestWithAPIName:[self.child apiMethodName] ......];
网络数据缓存
API数据可以做时间缓存,如缓存超时时间就是3分,三分钟内同一api请求数据为缓存数据,三分钟后为网络数据
再比如网络图片缓存,数据量基本上都特别大,这种就比较适合针对缓存大小来清理缓存的策略。
另外,之前的缓存的前提都是基于内存的。我们也可以把需要清理的缓存存储在硬盘上(APP的本地存储,我就先用硬盘来表示了,虽然很少有手机硬盘的说法,哈哈),比如前面提到的图片缓存,因为图片很有可能在很长时间之后,再被显示的,那么原本需要被清理的图片缓存,我们就可以考虑存到硬盘上去。当下次再有显示网络图片的需求的时候,我们可以先从内存中找,内存找不到那就从硬盘上找,这都找不到,那就发起请求吧。