Skip to content

Commit

Permalink
[refactor] 缓存决议时机更改为业务处理后
Browse files Browse the repository at this point in the history
  • Loading branch information
indulgeIn committed Dec 29, 2019
1 parent 7dd3ce0 commit 0a95cc0
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 28 deletions.
66 changes: 47 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Cocoapods](https://img.shields.io/cocoapods/p/YBNetwork.svg)](https://github.com/indulgeIn/YBNetwork) 
[![License](https://img.shields.io/github/license/indulgeIn/YBNetwork.svg)](https://github.com/indulgeIn/YBNetwork) 

基于 AFNetworking 二次封装,调用方便,设计简洁,易于拓展
基于 AFNetworking 二次封装,功能细致易拓展

设计原理博客:[谈谈 iOS 网络层设计](https://www.jianshu.com/p/fe0dd50d0af1)

Expand All @@ -16,6 +16,7 @@

- 支持缓存写入模式、读取模式、有效时长等自定义配置 (同时享有来着 YYCache 的优越性能)
- 支持发起请求和响应回调的预处理
- 支持网络落地异步重定向
- 重复请求处理策略选择
- 网络请求释放策略选择
- 支持 Block 和 Delegate 回调方式
Expand All @@ -24,18 +25,29 @@

## 安装

### CocoaPods

1. 在 Podfile 中添加 `pod 'YBNetwork'`
2. 执行 `pod install``pod update`

若搜索不到库,可使用 rm ~/Library/Caches/CocoaPods/search_index.json 移除本地索引然后再执行安装,或者更新一下 CocoaPods 版本。

### 手动导入

1. 下载 YBNetwork 文件夹所有内容并且拖入你的工程中。
2. 链接以下 frameworks:
* AFNetworking
* YYCache


## 用法

可下载 DEMO 查看示例。

### 基本使用

#### 1、第一步

创建子类继承自`YBBaseRequest`,并且在构造方法中初始化一些通用的配置 (比如服务器地址、解析器)
```
@interface DefaultServerRequest : YBBaseRequest
Expand All @@ -46,17 +58,17 @@
self = [super init];
if (self) {
self.baseURI = @"https://www.baidu.com";
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.requestSerializer.timeoutInterval = 30;
self.responseSerializer = [AFJSONResponseSerializer serializer];
}
return self;
}
- (AFHTTPRequestSerializer *)requestSerializer {...}
- (AFHTTPResponseSerializer *)responseSerializer {...}
@end
```
如果项目接口来自不同的接口团队(往往通用配置不同),那么就为每一个接口团队子类化一个`YBBaseRequest`,然后分别配置通用配置。

#### 2、第二步

创建具体接口配置,有两种方式,一种是直接实例化`DefaultServerRequest`,一种是继续子类化`DefaultServerRequest`:
```
//直接实例化
Expand All @@ -83,6 +95,7 @@ request.requestParameter = @{...};
```

#### 3、第三步

发起网络请求,设置回调:
```
//Block
Expand All @@ -104,7 +117,9 @@ request.delegate = self; //代理方法回调结果
注意:虽然 Block 方式就算不弱引用`self`也不会循环引用 (内部会回调完成后破除循环),但是为了避免延长`self`的生命周期,强烈建议使用弱引用,不然可能会导致网络请求释放策略失效。如果你是主导者,那么可以规定必须使用 Delegate 方式回调,从源头上避免延长网络响应接受者生命周期。



### 请求和响应预处理

往往我们需要为所有请求参数添加一些字段,比如设备ID,用户ID。
只需要在一级子类`DefaultServerRequest`中重载父类方法就行了:
```
Expand All @@ -124,44 +139,57 @@ request.delegate = self; //代理方法回调结果
- (void)yb_preprocessSuccessInMainThreadWithResponse:(YBNetworkResponse *)response {
//预处理请求成功
}
- (void)yb_preprocessFailureInChildThreadWithResponse:(YBNetworkResponse *)response {
//预处理请求失败
}
- (void)yb_preprocessFailureInMainThreadWithResponse:(YBNetworkResponse *)response {
//预处理请求失败
}
...
@end
```


### 重定向

网络落地重定向使用此方法:
```
- (void)yb_redirection:(void (^)(YBRequestRedirection))redirection response:(YBNetworkResponse *)response {
// 同步或异步的做一些事情
redirection(YBRequestRedirectionSuccess);
}
```
使用`redirection`闭包来达到可异步重定向的能力,在这之间可以做一些具体网络接口无感知的逻辑。


### 为响应对象添加属性

`YBNetworkResponse` 包含必要的响应数据,可以添加额外属性,在网络响应预处理时为这些属性赋值,那么具体接口调用方就可以很方便的拿到这些处理后的值了。建议创建一个`YBNetworkResponse`的分类,使用 Runtime 的关联属性来拓展。


### 缓存处理

缓存处理配置都在`request.cacheHandler`变量`YBNetworkCache`类中,支持以下配置:
- 内存/磁盘存储方式
- 缓存命中后是否继续发起网络请求
- 缓存的有效时长
- 定制缓存的 key
- 根据请求响应成功数据判断是否需要缓存(比如仅当 code=0 时数据有效允许缓存)
- 以及直接配置 YYCache
- 直接配置 YYCache

##### 缓存有效性验证

缓存命中提供了 Block 和代理方法的回调,一定要根据业务合理选择缓存机制,谨慎使用
内部会在业务处理完成网络响应数据后尝试进行缓存,且提供一个`shouldCacheBlock`可根据请求响应成功数据判断是否需要缓存(比如仅当 `code == 0` 时数据有效允许缓存)


### 重复网络请求处理策略

`request.repeatStrategy`变量配置,三种策略:
1. 允许重复网络请求
2. 取消最旧的网络请求
3. 取消最新的网络请求
- 允许重复网络请求
- 取消最旧的网络请求
- 取消最新的网络请求

举几个例子,当接口数据并不会在短时间变化时,重复发起网络请求就会浪费网络资源,可以选择方案 2 或 3;比如在搜索业务中,用户往往频繁的调用搜索接口,而发起一次搜索时,之前的搜索请求一般是没有意义了,就可以选用方案 2。


### 网络请求释放策略
`request.releaseStrategy`变量配置,有几种方式可以选择:
1. 网络任务会持有 YBBaseRequest 实例,网络任务完成 YBBaseRequest 实例才会释放
2. 网络请求将随着 YBBaseRequest 实例的释放而取消
3. 网络请求和 YBBaseRequest 实例无关联
- 网络任务会持有 YBBaseRequest 实例,网络任务完成 YBBaseRequest 实例才会释放
- 网络请求将随着 YBBaseRequest 实例的释放而取消
- 网络请求和 YBBaseRequest 实例无关联

举几个例子,若你的控制器出栈以后希望取消未落地的网络请求,那么就使用方案 2,注意管理好 YBBaseRequest 的生命周期就行了;若你的网络请求是不论如何都不希望它取消的,那么使用方案 3;若你希望网络请求任务始终持有 YBBaseRequest 实例避免它提前释放,那么使用方案 1。

Expand Down
2 changes: 1 addition & 1 deletion YBNetwork.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Pod::Spec.new do |s|


s.name = "YBNetwork"
s.version = "1.0.5"
s.version = "1.0.6"
s.summary = "基于 AFNetworking 网络中间层,注重性能,设计简洁,易于拓展"
s.description = <<-DESC
基于 AFNetworking 网络中间层,注重性能,设计简洁,易于拓展。
Expand Down
17 changes: 10 additions & 7 deletions YBNetwork/YBBaseRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,10 @@ - (void)clearRequestBlocks {
#pragma mark - request

- (void)startWithCacheKey:(NSString *)cacheKey {
__weak typeof(self) weakSelf = self;
BOOL(^cancelled)(NSNumber *) = ^BOOL(NSNumber *taskID){
__strong typeof(weakSelf) self = weakSelf;
if (!self) return YES;
YBN_IDECORD_LOCK(BOOL contains = [self.taskIDRecord containsObject:taskID];)
return !contains;
};
Expand Down Expand Up @@ -229,13 +232,6 @@ - (void)requestCompletionWithResponse:(YBNetworkResponse *)response cacheKey:(NS
}

- (void)successWithResponse:(YBNetworkResponse *)response cacheKey:(NSString *)cacheKey fromCache:(BOOL)fromCache taskID:(NSNumber *)taskID {

BOOL shouldCache = !self.cacheHandler.shouldCacheBlock || self.cacheHandler.shouldCacheBlock(response);
BOOL isSendFile = self.requestConstructingBody || self.downloadPath.length > 0;
if (!fromCache && !isSendFile && shouldCache) {
[self.cacheHandler setObject:response.responseObject forKey:cacheKey];
}

if ([self respondsToSelector:@selector(yb_preprocessSuccessInChildThreadWithResponse:)]) {
[self yb_preprocessSuccessInChildThreadWithResponse:response];
}
Expand All @@ -260,6 +256,13 @@ - (void)successWithResponse:(YBNetworkResponse *)response cacheKey:(NSString *)c
self.successBlock(response);
}
[self clearRequestBlocks];

// 在网络响应数据被业务处理完成后进行缓存,可避免将异常数据写入缓存(比如数据导致 Crash 的情况)
BOOL shouldCache = !self.cacheHandler.shouldCacheBlock || self.cacheHandler.shouldCacheBlock(response);
BOOL isSendFile = self.requestConstructingBody || self.downloadPath.length > 0;
if (!isSendFile && shouldCache) {
[self.cacheHandler setObject:response.responseObject forKey:cacheKey];
}
}

if (taskID) [self.taskIDRecord removeObject:taskID];
Expand Down
2 changes: 1 addition & 1 deletion YBNetworkDemo/TestCase/TestViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ - (void)searchA {

DefaultServerRequest *request = [DefaultServerRequest new];
request.cacheHandler.writeMode = YBNetworkCacheWriteModeMemoryAndDisk;
request.cacheHandler.readMode = YBNetworkCacheReadModeAlsoNetwork;
request.cacheHandler.readMode = YBNetworkCacheReadModeCancelNetwork;
request.requestMethod = YBRequestMethodGET;
request.requestURI = @"charconvert/change.from";
request.requestParameter = @{@"key":@"0e27c575047e83b407ff9e517cde9c76", @"type":@"2", @"text":@"呵呵呵呵"};
Expand Down

0 comments on commit 0a95cc0

Please sign in to comment.