这片文章也有塞尔维亚-克罗地亚语(由Jovana Milutinovich翻译)和日语(由@noradaiko翻译)json
若是有个一个网络库可以自动的为你处理cache该有多好啊。数组
若是有一个网络库可以在设备离线的时候自动的记住用户的操做该有多酷啊。缓存
当你离线的时候,你喜欢了一条微博或者把一条新闻标记为已读,而后网络库会在设备连网后自动执行这些操做,而且还不用写一行多余的代码。下面咱们就介绍MKNetworkKit能够作到这些。服务器
什么是 MKNetworkKit?
MKNetworkKit 是一个用objective-c写的网络库,具备无缝链接,基于block,ARC支持以及易用等特色。
MKNetworkKit的灵感来自于其余两个流行的网络库:ASIHTTPRequest和AFNetworking,结合了两个库的共同特色, 而且有一些新的特性。除此以外,MKNetworkKit可能会比其余网络库而言为了代码的清晰性,要求你写一丁点多的代码。用了 MKNetworkKit,你很难写出丑陋的网络代码。
特性
超轻量级
The complete kit is just 2 major classes and some category methods. This means, adopting MKNetworkKit should be super easy.
完整的库只有2个主类和一些category方法。也就是说,采用MKNetworkKit是很是容易的。
整个应用共享单一队列
严重依赖网络链接的应用应该优化他们的网络并发链接数。十分不幸的是,如今尚未网络库能够正确的完成这些功能。让我来举个例子说明若是你不去优化或者控制网络的并发链接数会发生什么。
Let’s assume that you are uploading a bunch of photos (think Color or Batch) to your server. Most
假如你正在上传一系列的图片(好比Color和 Batch) 到服务器。大多数的移动网络(3G)不容许一个给定的IP地址超过两个的并发的http请求。这就是说,在你的设备上,3G网络下,你不能同时打开超过两 个的并发HTTP请求。EDGE网络就更差了,大多数状况下你甚至不能打开超过一个的链接。这个限制在传统的wifi的状况下是至关高的(6个)。可是, 你的设备并不老是链接到Wifi下,你应该为受限制的网络环境考虑。在最普通的状况下你的设备都是链接到3G网络,就是说你被限制同时只能上传2张图片。 如今问题的关键不是上传两张图片时很慢,而是当你上传图片时再打开一个新的View,这个view在加载图片的缩略图的时候。当你不去经过app控制正确 的队列大小时,你的缩略图加载操做就会超时,这种现象可不是正确的。正确的作法是把缩略图的加载排好优先级,或者等待上传完成后再加载缩略图。这就要求你 的app有一个全局的队列。MKNetworkKit自动的保证你的app的每个队列的实例使用单一的共享队列。虽然MKNetworkKit本身不是 单例的,可是他的共享队列是。
正确的显示网络链接的标志
如今有许多第三方的类使用记录网络调用的次数的方式来控制网络连接标志的显示。但MKNetworkKit使用的是单一共享队列原则来控制网络标志的显示,即经过KVO注册共享队列里正在运行的操做。做为一个开发者,妈妈不再用担忧手动设置网络链接标志的问题了。
if (object == _sharedNetworkQueue && [keyPath isEqualToString:@"operationCount"]) { [UIApplication sharedApplication].networkActivityIndicatorVisible = ([_sharedNetworkQueue.operations count] = 0); }
Auto queue sizing 自动队列大小
咱们继续前一个讨论。我说了移动网络不容许同时超过两个并发网络请求。因此当3G网络时你的队列大小应该设为2,MKNetworkKit自动为你 处理这些。当网络进入3G/EDGE/GPRS时,它会更改并发链接的数目为2,而且在链接wifi时自动设置回6。有了这个特性,你会看到当你经过3G 网络从服务器加载缩略图(或者多个小的请求)时有巨大的性能提高
Auto caching 自动缓存
MKNetworkKit能够自动缓存你的全部GET请求。当你发起一样一个请求的时候,MKNetworkKit会当即用缓存的响应(若是有)来 调用完成方法,也会向远程服务器发出一个请求。当服务器的数据返回之后,会用取到的新的相应再次调用完成方法。这就是说,你不须要手动的处理缓存,你须要 作的,就只有调用这个方法:
[[MKNetworkEngine sharedEngine] useCache]; |
固然,你也能够在你的MKNetworkEngine子类中定制缓存的目录和内存缓存的消耗量。
Operation freezing 操做冻结
用了MKNetworkKit,你就拥有了冻结网络操做的能力。当你冻结一个操做的时候,为了防止网络链接丢失,操做会被自动序列化而且在设备联网以后自动执行。想一想微博客户端的草稿箱功能。
当你发送一条微博时,把这个网络操做标记为冻结,MKNetworkKit会自动的处理冻结和解冻工做!而后这条微博你不用写一行代码就会自动稍后 发送。你可使用在好比标记一条微博为喜欢或者从google reader客户端分享一篇文章或者添加一个链接到instpaper等相似场景中。
Image Caching 图片缓存
MKNetworkKit能够无缝的缓存缩略图。经过重写几个方法,你能够设置缓存多少张图片在内存缓存以及缓存的目录。重写这些方法彻底是可选的。
Performance 性能
一个词:速度。MKNetworkKit的缓存是无缝的,就像NSCache同样工做,除此以外,当内存警告的时候,内存缓存会写入到缓存目录中。
Full support for Objective-C ARC 全面支持Ojbective-C ARC
通常你会为新的项目选择一个新的网络库。MKNetworkKit不是为了取代你现有的(固然你也能够取代,可是可能会很麻烦)。在新的项目中,你 会很想要使用ARC功能。MKNetworkKit多是惟一全面支持ARC的网络库。基于ARC管理的代码速度可能比非ARC的代码是数量级的差距。
How to use 如何使用
OK,不自吹了,让咱们来看看如何使用这个framework。
Adding the MKNetworkKit 添加MKNetworkKit
- 拖拽MKNetworkKit的目录到你的工程里
- 添加 CFNetwork.Framework, SystemConfiguration.framework and Security.framework 依赖库
- 在PCH文件中包含 MKNetworkKit.h
- 删除 NSAlert+MKNetworkKitAdditions.h若是你开发的是iOS程序。
- 删除 UIAlertView+MKNetworkKitAdditions.h若是你开发的是Mac程序。
就是这样子,仅仅5个核心文件,妈妈不再用担忧网络请求了。
MKNetworkKit 中的类
- MKNetworkOperation
- MKNetworkEngine
- 混杂的帮助类 (苹果的 Reachability) 以及一些cateogory方法。
我相信简洁就是美。苹果已经把实际网络操做的繁重工做都作了,因此第三方的网络库应该提供的就是一个优雅的队列和可选的缓存。我坚信,任何第三方的 库都应该少于10个类(无论是网络库仍是UIkit的替代库)。多过10个,就是过分。Three 20库就是一个臃肿的例子,ShareKit也是,也许这两个库不错,可是仍然是庞大并且臃肿的。ASIHttpRequest或者 AFNetworking比RESTKit而言都是轻量级的,JSONKit比起TouchJSON(或者其余任何TouchCode库)就是轻量级的。 也许就我一我的喜欢这样子,但我就是不能忍受个人app里有第三方的库比个人代码还要多。
巨大的库的问题就在于很难去理解他的内在的工做机制,并且很难去根据本身的需求定制。个人添加IAP的MKStoreKit库就是超级易用的并且我 相信MKNetWorkKit也是如此。使用MKNetworkKit,你只须要知道MKNetworkOperation 和 MKNetworkEngine两个类所暴露出来的方法。MKNetworkOperation相似于ASIHttpRequest类,是一个 NSOperation的子类而且封装了请求相应类。你须要为你的app的每个网络请求建立一个MKNetworkOperation。
MKNetworkEngine是一个假单例的类,负责管理你的app的网络队列。所以,简单的请求时,你应该直接使用 MKNetworkEngine的方法。在更为复杂的定制中,你应该集成并子类化它。每个MKNetworkEngine的子类都有他本身的 Reachability对象来通知服务器的连通状况。你应该考虑为你的每个特别的REST服务器请求子类化MKNetworkEngine。由于是假 单例模式,每个单独的子类的请求,都会经过仅有的队列发送。
你能够在应用的delegate里面retain MKNetworkEngine的实例,就像 CoreDatademanagedObjectContext类。当你使用MKNetworkKit的时候,你建立一个MKNetworkEngine 的子类来从逻辑上分组你的网络请求。就是说,Yahoo相关的请求都在一个类中,Facebook相关的请求都在另外一个类中。咱们会看到3个不一样的使用库 的例子。
例1:
让咱们建立一个YahooEngine来获取雅虎财经的货币交换率。
第一步: 建立一个YahooEngine 继承自 MKNetworkEngine. MKNetworkEngine的初始化方法须要主机名和自定义的header(若是有)。自定义的头是可选的并且能够为nil,若是你正在编写本身的 REST服务器(不是如今的状况)你可能须要考虑添加客户端版本以及其余相似客户端标识的元数据。
NSMutableDictionary *headerFields = [NSMutableDictionary dictionary]; [headerFields setValue:@"iOS" forKey:@"x-client-identifier"]; self.engine = [[YahooEngine alloc] initWithHostName:@"download.finance.yahoo.com" customHeaderFields:headerFields]; |
注意,雅虎不须要你发送x-client-identifider在header里面,因此上面的例子仅仅是为了演示这个功能特性。
既然代码都是ARC的,那么是否对Engine的实例进行strong引用取决于开发者。
当你建立一个MKNetworkEngine子类,Reachability的会自动实现。这样当服务器宕机或者其余不可预料的状况下时,请求会被自动的队列或者冻结。想要获取更多关于冻结操做的信息,请阅读后面冻结操做相关的章节。
步骤 2:设计 Engine 类 (分离考虑)
让咱们开始在YahooEngine里面编写获取货币交换率的代码。Engine里面的方法会在ViewController里面调用。一个好的设 计实践就是确保你的Engine类不会暴露URL和http的请求头给调用的类。你的View层不该该知道url路径以及须要的参数,就是对应着 YahooEngine里面的货币及以及货币单位。返回的数据能够为double值表示货币交换率或者时间戳。由于请求都是异步的,你应该在block里 面返回这些值,好比:
-(MKNetworkOperation*) currencyRateFor:(NSString*) sourceCurrency inCurrency:(NSString*) targetCurrency onCompletion:(CurrencyResponseBlock) completion onError:(ErrorBlock) error; |
MKNetworkEngine父类定义了block的类型以下:
typedef void (^ProgressBlock)(double progress); typedef void (^ResponseBlock)(MKNetworkOperation* operation); typedef void (^ErrorBlock)(NSError* error); |
In our YahooEngine, we are using a new kind of block, CurrencyResponseBlock that returns the exchange rate. The definition looks like this.
在YahooEngine里面,咱们使用一种新的block,CurrencyResponseBlock来返回交换率的值,定义以下:
typedef void (^CurrencyResponseBlock)(double rate); |
在任何其余app里面,你都应该丁一你的block的方法相似于CurrencyResponseBlock这样的来给viewController传回值。
步骤 3: 处理返回数据
处理数据,就是转换你从服务器取回的数据,不论时Json仍是XML或者二进制plist,都应该在Engine里面完成。再次声明,不要让你的 Viewcontroller作这件事,你的Engine应该仅仅发回正确的model对象或者model对象的数组。在Engine中转换json或者 xml。再次声明,为了确保分类原则,你的viewController应该不知道从json里面获取单个元素的key。
这就是设计Engine的准则了,大部分的网络库不强迫你遵循一些接口分离的原则,但我do,由于我爱你。
步骤4: 方法实现
如今咱们讨论一下计算货币交换率的方法的具体实现细节
从yahoo获取交换率,就是一个简单的GET请求,我写了一个宏来定义获取货币率的url请求的格式
#define YAHOO_URL(__C1__, __C2__) [NSString stringWithFormat:@"d/quotes.csv?e=.csv&f=sl1d1t1&s=%@%@=X", __C1__, __C2__] |
你写的方法应该按照以下顺序执行:
- 准备好url和请求参数
- 建立一个请求的MKNetworkOperation对象.
- 设置方法参数
- 添加完成和错误处理方法,(完成方法就是你处理你响应到相应的model类的地方)
- 可选的,还有请求操做的进度指示。(或者在viewController里面处理)
- 若是你的操做时下载文件,那么设置一个下载的流(一般是一个文件)给他,这也是可选的
- 当请求操做完成时,处理结果而且调用block方法来向调用方法返回数据。
下面是实例代码:
MKNetworkOperation *op = [self operationWithPath:YAHOO_URL(sourceCurrency, targetCurrency) params:nil httpMethod:@"GET"]; [op onCompletion:^(MKNetworkOperation *completedOperation) { DLog(@"%@", [completedOperation responseString]); // do your processing here completionBlock(5.0f); }onError:^(NSError* error) { errorBlock(error); }]; [self enqueueOperation:op]; return op; |
上面的代码构造url而后建立了MKNetworkOperation,设置完完成方法和错误处理方法以后就把他经过调用父类的 enqueueOperation方法放入队列中,而且返回一个引用。你的viewController应该持有这个操做而且在 viewController弹出视图体系的时候取消网络操做。因此你能够在viewDidAppear里面调用engine的方法,而且在 viewWillDisappear里面取消操做。取消操做会释放队列从而使队列继续其余操做(记住,在移动网络中仅仅容许2个并发请求,取消不在须要的 操做能够提高性能,加速你的app)
你的ViewController也能够(可选的)添加进度处理,而且更新界面,下面就是如何作
[self.uploadOperation onUploadProgressChanged:^(double progress) { DLog(@"%.2f", progress*100.0); self.uploadProgessBar.progress = progress; }]; |
MKNetworkEngine也能够很方便的根据url来建立请求,因此下面代码能够写为:
MKNetworkOperation *op = [self operationWithPath:YAHOO_URL(sourceCurrency, targetCurrency)]; |
注意请求的url是自动在初始化engine的代码里面加上提供的域名。
建立一个POST,DELETE或者PUT的方法同样简单,就是更换http方法的参数。MKNetworkEngine也有更多给你多的便利指出好比读取header文件
示例 2:
上传图片到服务器(好比TwitPic)
如今咱们看一个如何上传图片到服务器的例子,上传图片明显须要操做把data编码为表格请求。MKNetworkKit遵循一系列的相似ASIHttpRequest的请求。
你能够调用addFile:forKey:在MKNetworkOperation里面来添加文件附件做为请求表格的数据。很简单。
MKNetworkOperation 也有一个简单的方法来从NSData指针中添加图片,就是调用addData:forKey: 方法来直接从NSData上传图片。 (好比直接从相机添加图片).
例子 3:
下载文件到本地目录(缓存)
用MKNetworkKit从远程服务器下载文件而且保存到用户的iPhone的某个地方是超级简单的
仅仅须要设置MKNetworkOperation的outputStream便可:
[operation setDownloadStream:[NSOutputStream outputStreamToFileAtPath:@"/Users/mugunth/Desktop/DownloadedFile.pdf" append:YES]]; |
你能够设置多个输出流到单个请求操做中来保存相同的文件到不一样的位置。(好比你想既保存到缓存目录又想保存到工做目录)
例子 4:
图片缩略图缓存。
为了下载图片,你可能须要提供绝对的url而不是一个相对目录
MKNetworkEngine有一个便捷的方法。只须要调用 operationWithURLString:params:httpMethodMKNetworkEngine 来用绝对url建立个一个请求,MKNetworkEngine是智能的,它会合并多个get请求到同一个url而且当操做完成时通知全部的block, 这极大的提升了获取缩略图的速度。
子类 MKNetworkEngine 而且重写图片缓存目录和缓存级别,若是你不想自定义这两个参数,你只须要调用MKNetworkEngine的方法来下载图片便可,实际上我也比较推荐这种方法,。
缓存操做
MKNetworkKit默认缓存全部的请求。你须要作的仅仅是在你的Engine上打开缓存。当GET请求执行时,若是响应以前被缓存过,你的完 成处理代码几乎是当即会被调用并传递缓存过的相应,要知道请求是否被缓存,调用isCachedResponse方法,好比下面
[op onCompletion:^(MKNetworkOperation *completedOperation) { if([completedOperation isCachedResponse]) { DLog(@"Data from cache"); } else { DLog(@"Data from server"); } DLog(@"%@", [completedOperation responseString]); }onError:^(NSError* error) { errorBlock(error); }]; |
冻结操做
能够肯定的, MKNetworkKit的最又去的功能就是内置的冻结操做的功能。全部你须要作的就是设置请求操做为freezable,不用费任何力气!
[op setFreezable:YES]; |
冻结的操做会在网络不通时自动的被序列化而且上线后自动执行。想一下在离线时标记一条微博为喜欢而后稍后上线以后操做会自动执行。冻结的操做也会被持久化到磁盘当app进入后台以后。而且会在稍后app恢复以后自动执行。
MKNetworkOperation中的便捷方法
MKNetworkOperation 提供了一些以下便捷方法来方便你格式化你的响应数据
- responseData
- responseString
- responseJSON (Only on iOS 5)
- responseImage
- responseXML
- error
从网络请求获取响应很方便,当返回格式错误时,这些方法返回nil,好比试图从响应为html中获取image会返回nil,惟一能够确保返回正确结果的方法时responseData,若是你肯定返回类型,能够用其余方法。
方便宏定义
宏定义Dlog和ALog是我不知羞耻的从stackoverflow上偷的,并且找不到源文件了,若是时你写的,请让我知道
关于GCD
我故意没用gcd,由于网络请求须要随时中止和安排优先级,gcd打给你然比NSOperationQueue更高效,可是不能作到上面两点。我不推荐在网络请求的队列中使用gcd。
关于文档
头文件都加了注释,并且我正在尝试整理,于此同事,你能够随时玩弄代码。
源代码
源代码和Demo工程都在Github,链接:MKNetworkKit on Github
关于新增特性
请不要email添加新特性,最好的方式是在github上添加issue
许可证
MKNetworkKit is licensed under MIT License
All of my source code can be used free of charge in your app, provided you add the copyright notices to your app. A little mention on one of your most obscure “about” page will do.
Attribution free licensing available upon request. Contact me at mknetworkkit@mk.sg