iOS WebServiceFramework网络服务框架浅解

  不知不觉毕业后也工作了快半年了,时光荏苒总觉得若再不写点什么,留下点痕迹,我都快把自己遗忘了。从接触iOS到现在初略算有一年多了吧,我很想把自己所看所想所领悟的一些技巧给大家分享,但自身开发经验也不算成熟,今天厚一厚脸皮,写自己的第一篇博客,我想只要不误人子弟,这次就算我赢了吧。下面进入正题——构建iOS的网络业务请求框架。

  网络服务几乎是每一款成功APP的必备条件,打开你手机你会发现里面不用联网的应用数量十只手指可以数出来,就算是一些以独特技术切入市场的APP如美颜相机,都至少加入了分享功能。下面我先做下简单的回顾兼扫盲。

  苹果定义了NSConnection 这个类来专门处理网络请求,并且这个类有两种用法来满足开发者需求。

用法一,同步请求  

 //NSURLConnection.h

 @interface NSURLConnection (NSURLConnectionSynchronousLoading)

  + (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error;

 @end

//**.m
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error];

这种用法比较少人使用,因为他无法捕捉传输过程的内容,并且需要在子线程中使用,不然将会卡住主线程的UI绘制,直到sendSynchronousRequest返回data,但胜在简单易用,一目了然。

用法二,异步请求  

//NSURLConnection.h

 @interface NSURLConnection : NSObject

 - (instancetype)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately;
 - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
 - (void)start;
@end
//**.m
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO]; 
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[connection start];

相比第一种用法,第二种顿时让人云里雾里,特别nsrunloop更让人疑问,另外,上面的代码仅仅是开始请求部分,数据接收部分的代码还没贴出来。

细心的人注意到,在.m文件中,NSConnection初始化时传入了一个delegate,是的,数据便是通过NSURLConnectionDataDelegate的回调来处理的。

//NSURLConnection.h
@protocol NSURLConnectionDataDelegate <NSURLConnectionDelegate>
@optional
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection; @end

为了不让其他函数扰乱了大家的实现,这里我只贴出跟本文有关的函数,从字面上大家都能知道这他们的作用了,didReceiveData会携带着请求下来的数据data多次回调,didFinishLoading是在请求结束后调用,另外还有一个didFailWithError在请求失败是调用,只不错他是老爸NSURLConnectionDelegate的内容。

下面我们回到第二种用法的.m文件,这里面涉及到runloop的执行机制,我在这里只解释为什么要这样设置。

Runloop有五种运行模式,这里我只说三种:

1,NSDefaultRunLoopMode: 默认的运行模式,除了NSConnection对象的事件;

2,UITrackingRunLoopMode: 用于跟踪触摸事件触发的模式(例如UIScrollView上下滚动),主线程当触摸事件触发时会设置为这个模式;

3,NSRunLoopCommonModes: 是一组常用的模式集合,简单来将就是模式集合,既然是集合,当然包括上面的两种模式。

默认情况下NSURLConnection,NSTimer都是运行1模式下,那么问题来了,假如这个时候用户刷一下屏幕,此时切换为模式2,而这时候刚好有数据请求下来,那么就悲剧了,didReceiveData不会回调。(如果NSTimer涉及精密的数据计算,这里也要注意下)解决办法就是替换成NSRunLoopCommonModes。

著名的SDWebImage在网络异步请求图片时也是使用第二种方法,个人觉得这种方法相当高效,在不创建新线程的情况下,依靠runloop来监听网络请求数据,如果同学们还是有点凌乱,不妨试下将NSURLConnection替换成NSTimer这样,其实原理都差不多。

另外这里有一点NSConnection在初始化的时候要注意startImmediately:不能设置为YES,如果你设置为YES,后在设置runloopmode是无效的。

现在理解了网络请求最具高效的方案(个人认为。。。),有没有感觉这些代理什么挺繁琐的,而一个APP不可能只有一个网络请求接口,这时候我们就需要一个管理者的角色,我们暂时将它命名为HttpManager。一个manager统领着一群connection,这时候你发现问题又来了,connection从外表上看长得都一样,没有可以标记的东西,于是,我们给他造个子类MyURLConnection 并且加多一个tag字段,来互相辨认。如图:

 

有了tag后,tag用什么来装填好呢,对tag唯一的要求就是不重复,这里我就直接使用url来做tag,因为除非奇葩需求,否侧url重复请求确实没有必要。

在didReceivaData中,_dataTagMap跟据tag,追加数据;

在sendHttpRequestWithUrl中,先检查_connectionTagMap中是否已经有以该url为key的对象了,有就说明已经这条请求,这条请求可以丢弃了。没有就new一个MyURLConnection 并将url作为tag,最后以url为key ,myconnection为value填入_connectionTagMap中;

在didFinishLoading中,根据connect的tag值,将对应的connection从_dataTagMap中提出来并发给对应的Model层处理,并从_dataTagMap和_connectionTagMap移除相应项;

好像听起来很简单的样子,实际上,也很简单。。。这里的发给Model层可能有些同学有迷糊了,什么叫发?大家注意到前面_connectionTagMap如果发现有重复直接丢弃即return,所以这要求HttpManager和与之交互的类必须低耦合,所以这里的发意思是指使用通知

 [[NSNotificationCenter defaultCenter] postNotificationName:KHTTPMgrNotificationSendRequestCompleted object:self userInfo:info];

info其实就一个NSDictionary,里面你想装什么就装什么,用过都知道。

今天先到这里吧,本来想写两种请求框架对比,结果只写了一种也只写了一半,只好下周末再补上对应的网络业务Model层以及另外一种请求框架了。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。