原文链接:http://www.tbqw.com/art/175230.html
HTTP/2,是HTTP协定发布后的首个更新,于2015年2月17日被批准。它采取了一系列优化技术来总体晋升HTTP协定的传输机能,如异步连接复用、头紧缩等等,堪称是当前互联网利用开发中,网络层次架构优化的必选方案之一。
如何在iOS下使用HTTP/2?
如何设计一个iOS的网络层架构?
与时俱进下,咱们的解决方案?
与业务相结合的网络层设计
与安全相干的网络层设计
数据输入
数据回调
数据转换
数据输入
首先是输入进程。业务数据调用网络层接口时可以称之为输入,这里一般会有两种情势的设计。
第一种对比常见,许多时候会被称为集中式的API处理,行将一些时常使用的网络层调用的代码封装成一到两个函数供上层调用。上层输入相干的参数便能获得相干的回调。如下列函数:
+ (void)networkTransferWithURLString:(NSString *)urlString
andParameters:(NSDictionary *)parameters
isPOST:(BOOL)isPost
transferType:(NETWORK_TRANSFER_TYPE)transferType
andSuccessHandler:(void (^)(id responseObject))successHandler
andFailureHandler:(void (^)(NSError *error))failureHandler {
// 封装AFN
}
typedef NS_ENUM(NSUInteger, DRDRequestMethodType) {
DRDRequestMethodTypeGET = 0,
DRDRequestMethodTypePOST = 1,
DRDRequestMethodTypeHEAD = 2,
DRDRequestMethodTypePUT = 3,
DRDRequestMethodTypePATCH = 4,
DRDRequestMethodTypeDELETE = 5
};
@interface DRDBaseAPI : NSObject
@property (nonatomic, copy, nullable) NSString *baseUrl;
@property (nonatomic, copy, nullable) void (^apiCompletionHandler)(_Nonnull id responseObject, NSError * _Nullable error);
- (DRDRequestMethodType)apiRequestMethodType;
- (DRDRequestSerializerType)apiRequestSerializerType;
- (DRDResponseSerializerType)apiResponseSerializerType;
- (void)start;
- (void)cancel;
...
@end
DRDAPIPostCall *apiPost = [[DRDAPIPostCall alloc] init];
[apiPost setApiCompletionHandler:^(id responseObject, NSError * error) {
}];
[apiPost start];
数据回调
说完了输入问题,接下来输出的设计。在输出部份的设计中,可以说是八仙过海各显神通。
网络层的传输大多以异步加载为主,即服务器响应后由网络层来负责将数据推给上层业务线程。在iOS的体系中,也提供了许多种方式用于这类场景的处理,例如直接播送的Notification、函数回调的delegate和最具特点的Block,都能够完成这类任务。那末采取哪类方式呢?在回答这个问题前咱们先来看看这几种方式其各自的优缺陷。
Notification
Notification,顾名思义的播送,其特色在于一对多地发送相干数据的通知。优点特别显明,易于实现;但缺陷也很显明,会损坏全部APP架构设计中的层次结构,造成跨层的调用以及处理。
Delegate
Delegate,最经常使用的的回调方式。优点是后期易于保护且不会造成跨层的调用;缺陷则是回调代码与输入的逻辑代码大部份时候不会放在一块儿,增添了一些后期浏览上的本钱。
Block
Block是OC语言中的特性,其优点刚好是Delegate的缺陷,即它让回调的代码能够以及调用的代码维持在相同位置,利于静态代码追踪以及逻辑思惟的持续。缺陷则在于容易造成循环援用(Retain Cycle);并且至于大型APP来讲,埋点这类AOP行动通常在Block中难以为继,且会造成Debug上的一些难题。
在Block的使用进程中,必定要注意使用weakSelf以及strongSelf来打破循环援用。否则酿成的内存泄露会造成后期排查的难题。
小结
或许读者看到这会更困惑了,究竞啥样的方案更佳?个人认为仍是要从业务需求动身来进行设计,从我本身而言我更喜爱Block+Notification的情势,然后在适量的时候辅以Delegate完成。
数据转换
数据回调的问题已基本解决,然而新的问题也摆在了咱们的眼前:上层该看到怎么的数据?
在这里咱们会发现特别多的利用场景,譬如大多数情况下,业务层都但愿返回的是与其本身相干的数据结构(Model实例),在这样的条件下能够特别处所便地对本地的数据进行相干的操作;而又譬如说查询一个操作结果的是与非,那末自身数据就只有一个yes或no,这时候候采取一个数据结构来包括便会显得繁杂以及臃肿;又或者是网络层采用了JSON-RPC这样的协定,返回回来的信息存在大量的冗余数据,但上层业务却是若水万千只取一瓢饮。
从以上各种场景中咱们可以看到,业务所需的数据情势特别多变,因而最佳的方式仍是交给上层自我去处理。一种常见的法子就是设定一个Delegate或Block进行返回数据的转换,将JSON或XML等格式转成所需要的数据格式以利便上层业务继续处理。无非我个人更偏向于在API自身就实现好这个Delegate或Block所描写的转换函数,这样会让API的层次更为清晰。下一节我会谈到咱们的处理方式。
业务协定
输入与配置
数据转换与输出
安全
NS_ASSUME_NONNULL_BEGIN
@protocol DRDRPCProtocol
- (nullable NSString *)rpcRequestUrlWithAPI:(DRDBaseAPI *)api;
- (nullable id)rpcRequestParamsWithAPI:(DRDBaseAPI *)api;
- (nullable id)rpcResponseObjReformer:(id)responseObject withAPI:(DRDBaseAPI *)api;
- (nullable id)rpcResultWithFormattedResponse:(id)formattedResponseObj withAPI:(DRDBaseAPI *)api;
- (NSError *)rpcErrorWithFormattedResponse:(id)formattedResponseObj withAPI:(DRDBaseAPI *)api;
@end
- (NSString *)requestMethod {
return @"get";
}
- (id)requestParameters {
return nil;
}
- (DRDRequestMethodType)apiRequestMethodType {
return DRDRequestMethodTypeGET;
}
- (DRDRequestSerializerType)apiRequestSerializerType {
return DRDRequestSerializerTypeHTTP;
}
- (DRDResponseSerializerType)apiResponseSerializerType {
return DRDResponseSerializerTypeHTTP;
}
DRDAPIGetCall *apiGet = [[DRDAPIGetCall alloc] init];
[apiGet setApiCompletionHandler:^(id responseObject, NSError * error) {
NSLog(@"responseObject is %@", responseObject);
if (error) {
NSLog(@"Error is %@", error.localizedDescription);
}
}];
[apiGet start];
DRDGeneralAPI *apiGeGet = [[DRDGeneralAPI alloc] initWithRequestMethod:@"get"];
apiGeGet.apiRequestMethodType = DRDRequestMethodTypeGET;
apiGeGet.apiRequestSerializerType = DRDRequestSerializerTypeHTTP;
apiGeGet.apiResponseSerializerType = DRDResponseSerializerTypeHTTP;
[apiGeGet setApiCompletionHandler:^(id responseObject, NSError * error) {
NSLog(@"responseObject is %@", responseObject);
if (error) {
NSLog(@"Error is %@", error.localizedDescription);
}
}];
[apiGeGet start];
- (nullable id)apiResponseObjReformer:(id)responseObject andError:(NSError * _Nullable)error;
@property (nonatomic, copy, nullable) id _Nullable (^apiResponseObjReformerBlock)(id responseObject, NSError * _Nullable error);
实例化一个DRDSecurityPolicy, 将SecurityPolicy中的DRDSSLPinningMode设置成为DRDSSLPinningModePublicKey或DRDSSLPinningModeCertificate。
将API的apiSecurityPolicy设定为以上实例。
将服务器的公钥证书放到APP Bundle中。
收场!
**多网络要求的并发履行。 **
假想这样一个场景,咱们但愿有若干个网络要求,当这些要求都收场后才通知上层利用工作的完成。
初期采用AFNetworking的AFHTTPRequestOperationManager方案时,AFN对全部系统都采取了NSOperation和NSOperationQueue来节制网络要求的依赖性。然而HTTP/2后的NSURLSession因为没法使用这类设计,因而造成这类并发依赖难以为继。
值得庆幸的是,AFNetworking在SessionManager的实现中照旧保存有dispatch_group_t的接口。因而咱们使用dispatch_group创立了DRDAPIBatchAPIRequests类,来到达并发网络要求的目的。
@interface DRDAPIBatchAPIRequests : NSObject
@property (nonatomic, strong, readonly, nullable) NSMutableSet *apiRequestsSet;
@property (nonatomic, weak, nullable) id delegate;
- (void)addAPIRequest:(nonnull DRDBaseAPI *)api;
- (void)addBatchAPIRequests:(nonnull NSSet *)apis;
- (void)start;
@end
@protocol DRDAPIBatchAPIRequestsProtocol
- (void)batchAPIRequestsDidFinished:(nonnull DRDAPIBatchAPIRequests *)batchApis;
@end