前言

前一段时间一直在学习iOS的架构。为什么呢?

公司的架构一直是MVC,当我们正式上线的时候,项目已经有了超十万行代码。主要的VC一般都有2000行代码以上。

关键是,目前版本我们只做了三分之一的业务逻辑…

所以,架构重构吧。

正文

MVVM

MVVM: Model-View-ViewModel

MVVM其实是MVC的进化版,它将业务逻辑从VC中解耦到ViewModel,来实现VC大’瘦身’。

用代码解释吧!

做一个简单的登录判断:

创建LoginViewModel(逻辑处理),LoginModel(只放数据),LoginViewController。

这里不用LoginView是为了让初学者能更好的把精力集中在用ViewModel解耦上。

当然要是你这些都明白,你可以直接看Wzxhaha/RandomerFramework,这是我在做的独立项目Randomer的基本架构(SubClasses+Protocol+MVVM+RAC)以及它的登录注册模块。另外,感谢王隆帅的这篇文章为我打开了新世界的大门。

在LoginModel中加入方法

//.h

- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password;

@property (nonatomic,copy,readonly)NSString * username;

@property (nonatomic,copy,readonly)NSString * password;

//.m

- (instancetype)initWithUserName:(NSString *)username password:(NSString *)password {

if (self = [super init]) {

_username = username;

_password = password;

}

return self;

}

这个没什么好讲的,就是给Model加一个初始化方法。

在LoginViewModel中加入方法

#import "PersonModel.h"

- (instancetype)initWithPerson:(PersonModel *)person;

@property (nonatomic,assign,readonly)BOOL canLogin;

- (instancetype)initWithPerson:(PersonModel *)person {

if (self = [super init]) {

//在这做你绑定model后的处理

_canLogin = [self valiCanLoginWithUserName:person.username password:person.password];

}

return self;

}

- (BOOL)valiCanLoginWithUserName:(NSString *)username password:(NSString *)password {

if (username.length & password.length) {

return YES;

} else {

return NO;

}

}

给ViewModel添加个绑定Model的初始化方法,以及判断帐号密码是否有效的方法。

然后VC(或者View)就可以直接这样获得判断后的结果

PersonModel * person = [[PersonModel alloc]initWithUserName:@"10" password:@"10"];

PersonViewModel * viewModel = [[PersonViewModel alloc]initWithPerson:person];

NSLog(@"%d",viewModel.canLogin);

简单的功能的时候没什么,当你处理复杂的逻辑判断的时候,MVVM会有巨大优势。

顺便讲一下ReactiveCocoa,我之所以这么推崇MVVM,主要就是因为RAC和MVVM简直太配了!

ReactiveCocoa

RAC具有函数式编程和响应式编程的特性,要是对编程思想不熟的可以看我的WZXProgrammingIdeas

RAC最大的用处就是能监听到各个事件,RAC把这个叫做信号流,然后接受信号通过block回调,里面大量的使用了block,所以一定要用好@weakify(self)和@strongify(self)。

为什么说RAC和MVVM太配了?

MVVM是把方法解耦到ViewModel,但是还是要VC(V)调用的,那么判断什么时候调用的逻辑还是会复杂。

而RAC解决了这个问题,它负责监听事件,然后调用ViewModel来进行逻辑判断。

例如:

[[_registerBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {

@strongify(self)

[self.viewModel toRegisterWithType:Register];

}];

[[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {

@strongify(self)

[self.viewModel loginWithUserName:self.usernameTextField.text password:self.usernameTextField.text Success:^(id response) {

} failure:^{

SHOW_ERROR(@"错误", @"账号或密码错误")

} error:^(NSError *error) {

SHOW_ERROR(@"错误", @"网络连接失败")

}];

}];

RAC监听了登录和注册按钮,使得代码简洁,而且结构十分紧凑。

Demo的话还是看这个吧Wzxhaha/RandomerFramework

https://github.com/Wzxhaha/RandomerFramework

或者简单版的WZXRACDemo

https://github.com/Wzxhaha/WZXRACDemo

链式网络请求框架

为什么封装WZXNetworking

这是一个容错性非常吓人的框架。

[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").RequestType(POST).HTTPHeader(nil).Parameters(nil).RequestSerialize(RequestSerializerHTTP).ResponseSerialize(ResponseSerializerJSON) startRequestWithSuccess:^(id response) {

NSLog(@"success");

} failure:^{

NSLog(@"failure");

}];

在这里除了.setRequest(url)和startRequestWithSuccess failure方法,其他都是非必要的。

你可以这样:

[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001") startRequestWithSuccess:^(id response) {

NSLog(@"success");

} failure:^{

NSLog(@"failure");

}];

链式在参数和参数的选择很多的情况或者很有可能改动的情况下展现了惊人的优势。因为,它的改动十分方便,只不过添加或者修改一个方法。

打个比方:

换成集中式API封装应该是这样的:

- (void)GET:(NSString *)url

parameters:(id)Parameters

success:(SuccessBlock)success

failure:(FailureBlock)failure;

当你要添加一个Version属性做API版本判断的时候,你能怎么办?只能重写方法,在方法中加入一个Version参数,然后所有使用的网络请求都要改变方法。

换成分布式API封装我们则不考虑对比了..

GeneralAPI *apiGeGet            = [[GeneralAPI alloc] initWithRequestMethod:@"get"];

apiGeGet.apiRequestMethodType      = RequestMethodTypeGET;

apiGeGet.apiRequestSerializerType  = RequestSerializerTypeHTTP;

apiGeGet.apiResponseSerializerType = ResponseSerializerTypeHTTP;

[apiGeGet setApiCompletionHandler:^(id responseObject, NSError * error) {

NSLog(@"responseObject is %@", responseObject);

if (error) {

NSLog(@"Error is %@", error.localizedDescription);

}

}];

[apiGeGet start];

这样的结构是否太松散?

再换成WZXNetworking

我们要做的只是再添加一个方法和一个成员变量,然后在原有方法后面加一个.method()

- (WZXNetworkManager * (^) (id some))method {

return ^WZXNetworkManager (id some) {

self.XXX = some

return self;

}

}

[[WZXNetworkManager manager].setRequest(@"http://192.168.1.40:8001").method(some) startRequestWithSuccess:^(id response) {

NSLog(@"success");

} failure:^{

NSLog(@"failure");

}];

代码放这:WZXNetworking

https://github.com/Wzxhaha/WZXNetworking

至于链式是怎么实现的,还是看那个WZXProgrammingIdeas

https://github.com/Wzxhaha/WZXProgrammingIdeas

谈谈MVVM和链式网络请求架构的更多相关文章

  1. Swift基础之使用Alamofire库进行网络请求和断点下载

    好久没有写过Swift相关的文章博客了,这里我就展示一下关于使用Alamofire库的方法 1.什么是Alamofire (1)Alamofire 的前身是 AFNetworking.AFNetwor ...

  2. 简谈 JavaScript、Java 中链式方法调用大致实现原理

    相信,在 JavaScript .C# 中都见过不少链式方法调用,那么,其中实现该类链式调用原理,大家有没有仔细思考过?其中 JavaScript 类库:jQuery 中就存在大量例子,而在 C# 中 ...

  3. 2020,最新APP重构:网络请求框架

    在现在的app,网络请求是一个很重要的部分,app中很多部分都有或多或少的网络请求,所以在一个项目重构时,我会选择网络请求框架作为我重构的起点.在这篇文章中我所提出的架构,并不是所谓的 最好 的网络请 ...

  4. Rxjava2.0 链式请求异常处理

    使用Rxjava2.0的过程中,难免会遇到链式请求,而链式请求一般都是第一个抛异常,那么后面的请求都是不会走的.现在来讨论一下链式请求的一种异常处理方法.例如: 一个登录-->通过登录返回的to ...

  5. 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别

    一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...

  6. ios MVVM实践 刷新网络请求+tableView展示数据

    [实现效果] [目录结构相关] 此示例展示用的是MVVM结构形式,表述如下 M:数据Model的存储,可以用来对属性进行处理.(即胖model概念,上图中xx万人订阅这个处理方法写在Model内) V ...

  7. Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析

    Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...

  8. 谈谈 Objective-C 链式语法的实现

    引言 对于 Objective-C 的语法,喜欢的人会觉得它是如此的优雅,代码可读性强,接近自然语言,开发者在调用大多数方法时不需要去查看注释或文档,通常只凭借方法名就可以大致知道这个方法的作用,可以 ...

  9. 使用Retrofit2+RxJava2+ProtoBuf实现网络请求

    引言 Retrofit 是一个用于 Android 和 Java 平台的类型安全的,底层使用OkHttp实现网络请求框架.Retrofit 通过将 API 抽象成 Java 接口而让我们连接到 RES ...

随机推荐

  1. ubuntu终端提示符@name修改

    需要修改两个文件: 1.在终端输入 vim  /etc/hosts 将当前的name改为ubuntu: 2.在终端输入 vim /etc/hostsname 将当前的name改为ubuntu: 3.重 ...

  2. svn版本控制-windows篇

    一.准备工作 1.获取 Subversion 服务器程序(服务端) 到官方网站(http://subversion.tigris.org/)下载最新的服务器安装程序.目前最新的是1.5版本,具体下载地 ...

  3. 【转】java list用法示例详解

    转自:http://www.jb51.net/article/45660.htm java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对java list用法做了详解. Lis ...

  4. selenium Grid

    Selenium Grid 的机制是启动一个 hub,然后启动多个 Selenium RC 注册到 hub 上, 当测试请求到 hub 时,hub 会将测试分发给 Selenium RC, Selen ...

  5. 设计模式_Composite_合成模式

    形象例子: Mary今天过生日.“我过生日,你要送我一件礼物.”“嗯,好吧,去 商店,你自己挑.”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买.”“喂,买了三件了呀,我只答应送一件礼物的哦 ...

  6. linux 安装mongodb

    Linux 安装mongodb 1.下载mongodb linux wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon- ...

  7. 如何实现CSS居中?–CSS居中常用方法

    来源:http://www.ido321.com/824.html 一.水平居中 1.内联元素居中:相对父级块级元素居中对齐 1: .center-children { 2: text-align: ...

  8. [原创]git使用入门

    创建git项目并初始化 建立一个新文件夹,然后将该文件夹定义为git项目 Lilis-MacBook-Pro:GitDir lili$ mkdir testgit Lilis-MacBook-Pro: ...

  9. Struts2 url传递中文出现乱码

    项目所有的编码都改为了utf-8.在tomcat的 server.xml中修改下面这段 <Connector port="8080" protocol="HTTP/ ...

  10. leetcode@ [307] Range Sum Query - Mutable / 线段树模板

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...