谈谈MVVM和链式网络请求架构
前言
前一段时间一直在学习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和链式网络请求架构的更多相关文章
- Swift基础之使用Alamofire库进行网络请求和断点下载
好久没有写过Swift相关的文章博客了,这里我就展示一下关于使用Alamofire库的方法 1.什么是Alamofire (1)Alamofire 的前身是 AFNetworking.AFNetwor ...
- 简谈 JavaScript、Java 中链式方法调用大致实现原理
相信,在 JavaScript .C# 中都见过不少链式方法调用,那么,其中实现该类链式调用原理,大家有没有仔细思考过?其中 JavaScript 类库:jQuery 中就存在大量例子,而在 C# 中 ...
- 2020,最新APP重构:网络请求框架
在现在的app,网络请求是一个很重要的部分,app中很多部分都有或多或少的网络请求,所以在一个项目重构时,我会选择网络请求框架作为我重构的起点.在这篇文章中我所提出的架构,并不是所谓的 最好 的网络请 ...
- Rxjava2.0 链式请求异常处理
使用Rxjava2.0的过程中,难免会遇到链式请求,而链式请求一般都是第一个抛异常,那么后面的请求都是不会走的.现在来讨论一下链式请求的一种异常处理方法.例如: 一个登录-->通过登录返回的to ...
- 【读书笔记】iOS网络-同步请求,队列式异步请求,异步请求的区别
一,同步请求的最佳实践. 1,只在后台过程中使用同步请求,除非确定访问的是本地文件资源,否则请不要在主线程上使用. 2,只有在知道返回的数据不会超出应用的内存时才使用同步请求.记住,整个响应体都会位于 ...
- ios MVVM实践 刷新网络请求+tableView展示数据
[实现效果] [目录结构相关] 此示例展示用的是MVVM结构形式,表述如下 M:数据Model的存储,可以用来对属性进行处理.(即胖model概念,上图中xx万人订阅这个处理方法写在Model内) V ...
- Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析
Java生鲜电商平台-SpringCloud微服务架构中网络请求性能优化与源码解析 说明:Java生鲜电商平台中,由于服务进行了拆分,很多的业务服务导致了请求的网络延迟与性能消耗,对应的这些问题,我们 ...
- 谈谈 Objective-C 链式语法的实现
引言 对于 Objective-C 的语法,喜欢的人会觉得它是如此的优雅,代码可读性强,接近自然语言,开发者在调用大多数方法时不需要去查看注释或文档,通常只凭借方法名就可以大致知道这个方法的作用,可以 ...
- 使用Retrofit2+RxJava2+ProtoBuf实现网络请求
引言 Retrofit 是一个用于 Android 和 Java 平台的类型安全的,底层使用OkHttp实现网络请求框架.Retrofit 通过将 API 抽象成 Java 接口而让我们连接到 RES ...
随机推荐
- codeforces 671C Ultimate Weirdness of an Array 线段树+构造
题解上说的很清楚了,我照着写的,表示膜拜题解 然后时间复杂度我觉得应该是O(nlogn),虽然常数略大,预处理和倒着扫,都是O(nlogn) #include <stdio.h> #inc ...
- pomelo环境搭建
在ubuntu上搭建pomelo环境 一 安装node.js 不要直接安装nodejs, 因为ubuntu上默认的nodejs版本是0.6的太老千万不要下tar包自己安装, 问题很多正确方法如下: ...
- HW6.7
public class Solution { public static void main(String[] args) { int[] count = new int[10]; int numb ...
- POJ1149 PIGS
想了好久啊...(#-.-) 开始想到m*n个点的构图,明显超时,于是考虑压缩节点个数 我们发现每个猪圈最后被有且只有一个人调整,于是想到对于一个人,连接他能调整的每个猪圈的上一个控制人.(不懂可以开 ...
- Java TreeMap 源码解析
继上篇文章介绍完了HashMap,这篇文章开始介绍Map系列另一个比较重要的类TreeMap. 大家也许能感觉到,网络上介绍HashMap的文章比较多,但是介绍TreeMap反而不那么多,这里面是有原 ...
- iOS block并发
多核运算 在iOS中concurrency编程的框架就是GCD(Grand Central Dispatch), GCD的使用非常简单.它把任务分派到不同的queue队列来处理.开发者把任务代码装到一 ...
- jQuery each的实现与call方法的详细介绍
转载原出处: http://www.f2es.com/jquery-each-intro/ 先贴上jquery实现each功能的源代码(把常用的call部分提取出来,为了方便理解,就进行了一定的修改) ...
- html标签应用
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 虚方法(virtual)和抽象方法(abstract)的区别
注:本文转载自 http://www.cnblogs.com/michaelxu/archive/2008/04/01/1132633.html 虚方法和抽象方法都可以供派生类重写,它们之间有什么区别 ...
- 【Matlab】随机游走产生图像效果
随机游走类似布朗运动,就是随机的向各个方向走吧.产生的图像实在漂亮,所以还是贴出分享. clear all; close all; clc; n=100000; x= 0; y= 0; pixel=z ...