关于 iOS 相关技术的博客非常非常多了, 没有好的内容也不想写, 最近在迭代公司项目版本, 对于这个题材也想了很久, 看了很多类似的文章, 决定记录一下. 网络请求, 是客户端开发中一个很重要的模块, 关于此方面需要了解的东西也是非常多的, 此篇文章仅介绍自己用过的有关框架.
关于 iOS 相关技术的博客非常非常多了, 没有好的内容也不想写, 最近在迭代公司项目版本, 对于这个题材也想了很久, 看了很多类似的文章, 决定记录一下. 网络请求, 是客户端开发中一个很重要的模块, 关于此方面需要了解的东西也是非常多的, 此篇文章仅介绍自己用过的有关框架.
AFNetworking
1. 关于 AFNetworking
只要是做 iOS 开发的应该都是知道这个框架的, 有多优秀我就不赘述了. 自 iOS9 之后苹果弃用了 NSURLConnection 只用 NSURLSession, 所以 AFN 从 3.0 版本开始就删除了基于 NSURLConnection API 的所有支持, 基于 NSURLSession 框架以及 NSOperation 进行的封装开发.
2. 基于 AFNetworking 进行网络请求
既然用的是第三方框架, 那么肯定会有一些局限性. 框架的迭代更新都会影响我们的代码, 所以尽可能解耦, 一般我们都会单独的写一个网络请求工具类对框架进行封装. 这样即使框架更改了, 也只需要更改工具类相关代码.
a. 新建网络请求工具类, 实例化 AFHTTPSessionManager. 类似如下 :
- + (instancetype)sharedInstance
- {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _mutParamares = [NSMutableDictionary dictionary];
- _manager = [AFHTTPSessionManager manager];
- _manager.requestSerializer = [AFHTTPRequestSerializer serializer];
- _manager.requestSerializer.timeoutInterval = 30.0f;
- [AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
- _manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json",
- @"text/plain", @"text/javascript", @"text/xml", @"image/*", nil];
- });
- }
两个需要注意的问题: 第一, 这里的网络请求工具类是一个单利, 为什么要用单利呢? [AFHTTPSessionManager manager]跟踪到这个方法里, 会看到返回的 manager 并不是单利, 如果每次请求都实例化一个 manager 的话, 那么有可能造成内存泄漏. 第二, 有时候请求失败的原因是 AFN 支持的 response 类型和服务器返回给我们的类型不一致, 需要修改 AFN 的源码进行修改, 但用 Cocoapods 来管理三方框架, pod update 之后修改的代码又会被重置. 此时, 就可以通过 acceptableContentTypes 属性来根据需要设置.
- + (instancetype)manager {
- return [[self alloc] initWithBaseURL:nil];
- }
也可以通过 requestSerializer 属性设置请求头相关的信息. 如:
[self.manager.requestSerializer setValue:@""forHTTPHeaderField:@""];
b. 常用的网络请求类型. 这是对外的 API, 外部通过调用这些接口实现相关的网络请求. 当然你也可以根据自己的需要暴露相关的 API. 相应的接口实现比较简单. 具体的逻辑还应根据业务需求在外部实现. 对于文件的操作, 如图片的上传, 可参考代码如下.
- image.png
- // 上传多张图片
- [_manager POST:url parameters:param constructingBodyWithBlock:^(id _Nonnull formData) {
- for (UIImage *image in imgArray) {
- UIImage *resizeImage = image.reSizeImage;
- NSData *data = UIImagePNGRepresentation(resizeImage);
- [formData appendPartWithFileData:data name: @"file" fileName:[NSString stringWithFormat:@"img%ld.png",i] mimeType:@"image/png"];
- }];
上传多张图片的时候, 根据需要可对图片进行裁剪和压缩. 关于上传的进度可以通过 progressBlock 返回在对应的 UI 上进行显示, 进度的计算公式如下.
processBlock(progress.completedUnitCount / progress.totalUnitCount);
c. 监测网络状态.
- AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
- [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
- switch (status) {
- case AFNetworkReachabilityStatusUnknown: break;
- case AFNetworkReachabilityStatusNotReachable: break;
- case AFNetworkReachabilityStatusReachableViaWWAN: break;
- case AFNetworkReachabilityStatusReachableViaWiFi: break;
- }
- }];
- [manager startMonitoring];
以上是基于 AFNetworking 的网络请求. 上面说的这种网络请求方式是集约式的网络请求, 也就是所有的 API 都调用的是这个工具类. 还有一种网络请求方式是离散式的, 也就是每一个 API 都有自己对应的类.
YTKNetwork
1. 关于 YTKNetwork
这个框架也是基于 AFNetworking 进行的再次封装, 适用于规模较大的项目中. YTKNetwork 是离散式的网络请求方式, 如上所述, 每一个请求都对应一个对象, 可根据需要对相应的请求进行定制化. 另外 YTKNetwork 支持批量网络请求发送并设置统一回调, 支持相互依赖的网络请求等等功能.
2. 基于 YTKNetwork 进行网络请求
a. 同上, 不建议直接使用第三方, 自己写一个 BaseRequest 类继承 YTKRequest, 在这个类里面实现下面这个方法. 这个方法是所有请求的 Response.
- (void)startWithCompletionBlockWithSuccess:(YTKRequestCompletionBlock)success failure:(YTKRequestCompletionBlock)failure{}
b. 写一个网络请求配置类. 在程序启动的时候通过 YTKNetworkConfig 配置网络请求. 如 baseUrl 参数等. 通过 YTKNetworkAgent 设置一些参数, 如上面提到的 acceptableContentTypes 参数. 这个类是真正发起请求的类, 也是在这个类中与 AFN 打交道.
- _config = [YTKNetworkConfig sharedConfig];
- _config.baseUrl = BASE_INTERFACE_URL_DEV;
- YTKNetworkAgent *agent = [YTKNetworkAgent sharedAgent];
- NSSet *acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/plain", @"text/html", @"text/CSS", nil];
- NSString *keypath = @"jsonResponseSerializer.acceptableContentTypes";
- [agent setValue:acceptableContentTypes forKeyPath:keypath];
3. 以一个具体的 API(请求用户信息)请求为例.
3.1 新建一个 UserInfoAPI 类, 继承你写的 BaseRequest 类.
3.2 实现以下方法. 请求的 URL, 和外部无关, 不需要外面传进来. 请求的类型, 以及请求的参数. 当然, 解析也可以在 API 类内部实现. 通过在 GET 方法里面实现数据的转换和解析.
- - (NSString *)requestUrl {
- return kUserURL;
- }
- - (YTKRequestMethod)requestMethod {
- return YTKRequestMethodPOST;
- }
- - (instancetype)requestArgument {
- return parameter;
- }
serverRespData 是基类自定义的一个参数, 是数据过滤之后的 response.
- _serverRespData = [self.responseJSONObject objectForKey:@"data"];
- - (QDZQUseModel *)user { // 重写 user 的 get 方法.
- _user = [QDZQUserEntity yy_modelWithDictionary:[self.serverRespData objectForKey:@"appUser"]];
- return _user;
- }
3.3 如何调用这个 API
- + (void)fetchUserInfoSuccess:(void (^)(void))success failure:(void (^)(NSError * error))failure {
- UserInfoApi *api = [[UserInfoApi alloc] init];
- [api startWithCompletionBlockWithSuccess:^(__kindof YTKBaseRequest * _Nonnull request) {
- if (success) { }
- } failure:^(__kindof YTKBaseRequest * _Nonnull request) {
- if (failure) { }
- }];
- }
3.4 添加请求头
如果你需要添加请求头的话, 你可以实现下面这个方法.
- - (nullable NSDictionary *)requestHeaderFieldValueDictionary {
- return @{@"token" : @""};
- }
以上, 是两种网络请求方式(离散式, 集约式).
来源: http://mobile.51cto.com/hot-572220.htm