美文网首页
iOS OC项目优化之路(二)之Network篇

iOS OC项目优化之路(二)之Network篇

作者: boy丿log | 来源:发表于2019-06-04 13:44 被阅读0次

网络框架选型

AFNetworking

前后端交互是我们日常开发经常遇到的情况,在iOS中最知名的网络框架应该就是AFNetworking了。使用它可以让你从服务器很轻松的得到你所需要的数据,而不需要再去使用繁琐的NURLSession,它的用法如下:

[[AFHTTPSessionManager manager] GET:@"" parameters:@{} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

}failure:^(NSURLSessionDataTask*_Nullabletask,NSError*_Nonnullerror) {

}];

我们只需要传入url和para,就可以处理成功或失败的回调了。

AFNetworking的问题

对于AFHTTPSessionManager的处理,一般是保存一个全局的单例。但是这样的话会造成一个问题:

  1. 修改单个请求属性会影响到全局属性的修改,如过期时间。
  2. 无法获取到当前请求的状态,如正在请求中、请求失败或是成功。

XPNetworking

XPNetworking是我为了解决AFNetworking的这两个问题而写的基于面向对象的网络请求框架,它不但可以方便的自定义每一个请求,还基于YYCache的封装扩展了一个缓存的功能。它的使用方法如下:

    XPBaseRequest *request = [XPBaseRequest new];
    request.route = @"xx";
    request.path = @"xx";
    request.baseUrl = @"http://www.xx.com:8080";
    request.requestTimeOut = 10;
    [request startCompletionBlockWithSuccess:^(XPBaseRequest * _Nonnull request) {
        
    } failure:^(XPBaseRequest * _Nonnull request) {
        
    }];

针对网络缓存,它提供了两种缓存方式:磁盘缓存和内存缓存,它所支持的过期模式是存储日期过后的N秒时间。设置方法如下:

    request.diskCacheTime = 100;
    request.cacheType = XPCacheDisk | XPCacheMemory;
    request.memoryCacheTime = 100;
优点
  • 面向对象,可以通过基类定制划分业务接口
  • 请求对象高度自定制
  • 保留请求状态,可方便进行页面数据再处理
  • 集成缓存
    ①可方便定制缓存策略
    ②异步获取缓存,高并发不会阻塞主线程
    ③自动清理过期缓存
  • 同时间相同请求只会处理一条,但返回结果都会返回
  • 支持多请求结果同时处理以及多请求同步进行
  • 支持上传、断点续传

实现原理

XPNetworking库的目录结构是这样的:

  • XPBaseRequest,提供请求基本信息,状态的保存,对返回数据的处理
  • XPChainRequest,同步请求
  • XPBatchRequest,并发请求
  • XPRequestNetworkManager,发起网络请求的类,用来管理XPRequestUniquePackage对象
  • XPRequestUniquePackage,网络请求管理队列,用来处理同时进行多个相同请求的情况
  • XPRequestSessionManager,执行网络请求的类
  • XPRequestConfiguration,全局配置的类

主要从两个方面说起,一个是请求,另一个是缓存。

缓存

之所以将缓存封装进网络请求框架是因为iOS许多页面都是需要缓存一些数据,以便用户在进入app可以看到页面上的内容,而不是空白页面,优化用户体验。在之前的做法中,缓存与网络请求是分开的,在请求之前先取数据,然后再去缓存,存储的数据错综复杂,很有可能处理逻辑与从网络请求获取的数据处理逻辑不相同,这样就造成了代码的冗余,以及许多不必要的麻烦。

这里的缓存框架用的是YYCache,他是一个基于sqlite的高效缓存框架。它可以让我们不需要操作数据库就可以达到缓存数据的效果。但在使用过程中我发现有两点问题:

  1. 它的缓存模式并不是我需要的,它的缓存过期时间是基于使用时间,而我需要的是基于缓存时间。
  2. 它开启了wal,会在一定的情况下造成app占用内存过高。

所以针对这两个问题,我封装了一个管理YYCache的工具:
XPCache。

它的API基本与YYCache相同。它实现缓存过期机制实际上就是在YYCache中预留一个字段用来保存其他数据的缓存设置:

     NSString *cacheFolder = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    NSString *path = [cacheFolder stringByAppendingPathComponent:XPCachePathName];
    _diskCache = [[YYDiskCache alloc] initWithPath:path];
    _memoryCache = [YYMemoryCache new];
    _memoryCache.name = XPCachePathName;
    _cacheManager =  [XPCacheManager new];
    id data =  [_diskCache objectForKey:XPCacheManagerName];
    if ([data isKindOfClass:[NSDictionary class]]) {
        _cacheManager.diskData = [NSMutableDictionary dictionaryWithDictionary:data];
    }else{
        [_diskCache removeAllObjects];
    }
    _cacheManager.delegate = self;
    [_cacheManager start];

关于频繁调用造成内存过大的问题是通过在操作YYCache时,先进行自身一套机制处理,然后再去操作YYCache。这样就减少了YYCache的高频操作。

网络请求

BaseRequest

网络请求是基于面向对象的,在这里有一个BaseRequest,它包含的是一个网络请求最基本的一些元素:

一、发起请求
/**
 请求host
 */
@property (nonatomic, copy) NSString * baseUrl;

/**
 首路径
 拼接: baseUrl + route + path
 */
@property (nonatomic, copy) NSString * route;

/**
 路径
 */
@property (nonatomic, copy) NSString * path;

/**
 参数
 */
@property (nonatomic, strong) NSMutableDictionary * params;

/**
 请求方法
 */
@property (nonatomic, assign) XPRequestMethod  method;

在发起请求后,会根据设置的request来设置请求头和响应,在没有设置request的属性时,所使用的是全局属性。如下

- (AFHTTPRequestSerializer *)configRequestSerializerByRequset:(XPBaseRequest *)request{
    AFHTTPRequestSerializer * serializer = [self requestSerializerWithRequestSerializerType:request.requestSerializerType];
    NSArray *requestAuthorizationHeaderFieldArray = request.requestAuthorizationHeaderFieldArray;
    if (request.requestAuthorizationHeaderFieldArray) {
        [serializer setAuthorizationHeaderFieldWithUsername:[requestAuthorizationHeaderFieldArray firstObject] password:[requestAuthorizationHeaderFieldArray lastObject]];
    }
    serializer.allowsCellularAccess = request.allowsCellularAccess;
    NSDictionary *headerFieldValueDictionary = request.headerFieldValueDictionary?request.headerFieldValueDictionary:self.configuration.headerFieldValueDictionary;
    if (headerFieldValueDictionary) {
        for (NSString * httpHeaderField in headerFieldValueDictionary.allKeys) {
            NSString *value = [request.headerFieldValueDictionary objectForKey:httpHeaderField];
            [serializer setValue:value forHTTPHeaderField:httpHeaderField];
        }
    }
    serializer.timeoutInterval = request.requestTimeOut == 0?self.configuration.timeOut:request.requestTimeOut;
    self.manager.requestSerializer = serializer;
    if (request.securityPolicy) {
        self.manager.securityPolicy = request.securityPolicy;
    }else{
        self.manager.securityPolicy = self.configuration.securityPolicy;
    }
    return serializer;
}
- (AFHTTPResponseSerializer *)confiResponseSerializerByRequest:(XPBaseRequest *)request{
    XPResponseSerializerType type = request.responseSerializerType;
    switch (type) {
        case XPResponseSerializerTypeHTTP:
            
            return self.httpResponseSerializer;
            break;
        case XPResponseSerializerTypeJSON:{
            NSSet *set = [request.acceptableContentTypes allObjects].count > 0?request.acceptableContentTypes : self.acceptableContentTypes;
            self.jsonResponseSerializer.acceptableContentTypes = set;
            return self.jsonResponseSerializer;
            break;
        }
        case XPResponseSerializerTypeXMLParser:{
            NSSet *set = [request.acceptableContentTypes allObjects].count > 0?request.acceptableContentTypes : self.acceptableContentTypes;
            self.xmlResponseSerializer.acceptableContentTypes = set;
            return self.xmlResponseSerializer;
            break;
        }
        default:
            break;
    }
}
二、请求状态

然后用这个请求头发起请求。同时在发起请求的时候给定请求状态,一共有这几种:

typedef NS_ENUM(NSInteger, XPRequestRunningStatus) {
    XPRequestRunningStatus_unStart = 0,//未开始
    XPRequestRunningStatus_running,//请求中-单
    XPRequestRunningStatus_finish_Success,//请求成功
    XPRequestRunningStatus_finish_Failure,//请求失败
    XPRequestRunningStatus_finish_Cancel,
    
};

这是request对象所包含的状态属性:

/**
 运行状态
 */
@property (nonatomic, assign, readonly) XPRequestRunningStatus  runningStaus;
/**
 是否取消
 */
@property (nonatomic, assign, readonly) BOOL  isCancelled;

/**
 是否运行中
 */
@property (nonatomic, assign, readonly) BOOL  isExecuting;

/**
 是否已经结束
 */
@property (nonatomic, assign, readonly) BOOL  isFinshed;

@property (nonatomic, assign, readonly) BOOL  isUnstart;
/**
 是否是请求失败
 */
@property (nonatomic, assign, readonly) BOOL  isFail;
三、同一请求同时发起时的合并

XPRequestUniquePackage类主要用来处理请求合并,当一个请求在发起到完成这段时间如果又发起一个相同的请求时,这个时候其实是没必要再进行网络请求的,这个时候可以把不需要发起的请求放入到队列中,在上一个请求完成后同时处理。

四、request对象自定制返回规则

相同的请求在处理时有可能会处理逻辑的测重点可能不同,request提供了处理自身返回结果的预处理能力。

/**
 json数据是否合法
 */
- (BOOL)validateJson;

在网络请求回调时会调用这个方法,在处理后返回YES则证明结果与预期相符,如果为NO则证明失败,会进入失败的回调。

五、请求的异步和同步

XPBatchRequest和XPChainRequest在内部处理了多请求的同步和异步。使用时只需要将请求加入到队列中,即可在回调中处理。

  XPBatchRequest *batch = [[XPBatchRequest alloc] initWithRequest:@[request]];
    [batch startCompletionBlockWithSuccess:^(XPBatchRequest *batchRequest) {
        
    }]; 

可在连获取到成功和失败的请求。

总结

面向对象的网络编程,可以让我们在与后端的交互中脉络更加清晰,减少资源的浪费,从而使我们的开发更加的高效。
感谢YTKNetwork提供的思路!

相关文章

网友评论

      本文标题:iOS OC项目优化之路(二)之Network篇

      本文链接:https://www.haomeiwen.com/subject/xsclxctx.html