iOS

iOS架构

2019-02-20  本文已影响15人  liboxiang

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
  • 如果是针对提供翻页机制的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的本地存储,我就先用硬盘来表示了,虽然很少有手机硬盘的说法,哈哈),比如前面提到的图片缓存,因为图片很有可能在很长时间之后,再被显示的,那么原本需要被清理的图片缓存,我们就可以考虑存到硬盘上去。当下次再有显示网络图片的需求的时候,我们可以先从内存中找,内存找不到那就从硬盘上找,这都找不到,那就发起请求吧。

上一篇 下一篇

猜你喜欢

热点阅读