首页 > 代码库 > iOS Framework: Introducing MKNetworkKit

iOS Framework: Introducing MKNetworkKit

MKNetworkKit介绍,入门。翻译

这片文章也有塞尔维亚-克罗地亚语(由Jovana Milutinovich翻译)和日语(由@noradaiko翻译) 
假设有个一个网络库可以自己主动的为你处理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是很easy的。 


整个应用共享单一队列 

严重依赖网络连接的应用应该优化他们的网络并发连接数。十分不幸的是。如今还没有网络库能够正确的完毕这些功能。让我来举个样例说明假设你不去优化或者控制网络的并发连接数会发生什么。

 

Let’s assume that you are uploading a bunch of photos (think Color or Batch) to your server. Most 

假如你正在上传一系列的图片(比方Color和 Batch)到server。大多数的移动网络(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网络从server载入缩略图(或者多个小的请求)时有巨大的性能提升 


Auto caching 自己主动缓存 

MKNetworkKit能够自己主动缓存你的全部GET请求。当你发起相同一个请求的时候。MKNetworkKit会马上用缓存的响应(假设有)来调用完毕方法,也会向远程server发出一个请求。当server的数据返回以后。会用取到的新的对应再次调用完毕方法。这就是说。你不须要手动的处理缓存,你须要做的,就仅仅有调用这种方法: 
[[MKNetworkEngine sharedEngine] useCache];
当然。你也能够在你的MKNetworkEngine子类中定制缓存的文件夹和内存缓存的消耗量。 


Operation freezing 操作冻结 

用了MKNetworkKit。你就拥有了冻结网络操作的能力。当你冻结一个操作的时候,为了防止网络连接丢失。操作会被自己主动序列化而且在设备联网之后自己主动运行。

想想微博client的草稿箱功能。 
当你发送一条微博时,把这个网络操作标记为冻结,MKNetworkKit会自己主动的处理冻结和解冻工作!然后这条微博你不用写一行代码就会自己主动稍后发送。你能够使用在比方标记一条微博为喜欢或者从google readerclient分享一篇文章或者加入一个连接到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 

  1. 拖拽MKNetworkKit的文件夹到你的project里
  2. 加入 CFNetwork.Framework, SystemConfiguration.framework and Security.framework 依赖库
  3. 在PCH文件里包括 MKNetworkKit.h
  4. 删除 NSAlert+MKNetworkKitAdditions.h假设你开发的是iOS程序。

  5. 删除 UIAlertView+MKNetworkKitAdditions.h假设你开发的是Mac程序。
就是这样子。只5个核心文件。妈妈再也不用操心网络请求了。 

 

MKNetworkKit 中的类 

  1. MKNetworkOperation
  2. MKNetworkEngine
  3. 混杂的帮助类 (苹果的 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对象来通知server的连通情况。你应该考虑为你的每个特别的RESTserver请求子类化MKNetworkEngine。由于是假单例模式。每个单独的子类的请求,都会通过仅有的队列发送。 


你能够在应用的delegate里面retain MKNetworkEngine的实例。就像CoreDatademanagedObjectContext类。当你使用MKNetworkKit的时候,你创建一个MKNetworkEngine的子类来从逻辑上分组你的网络请求。

就是说,Yahoo相关的请求都在一个类中,Facebook相关的请求都在还有一个类中。我们会看到3个不同的使用库的样例。 

 
例1: 
让我们创建一个YahooEngine来获取雅虎財经的货币交换率。

 

步骤 1: 创建一个YahooEngine 继承自 MKNetworkEngine. 

MKNetworkEngine的初始化方法须要主机名和自己定义的header(假设有)。自己定义的头是可选的并且能够为nil,假设你正在编写自己的RESTserver(不是如今的情况)你可能须要考虑加入client版本号以及其它类似client标识的元数据。 

    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的会自己主动实现。这样当server宕机或者其它不可预料的情况下时,请求会被自己主动的队列或者冻结。想要获取很多其它关于冻结操作的信息。请阅读后面冻结操作相关的章节。

 


步骤 2:设计 Engine 类 (分离考虑) 

让我们開始在YahooEngine里面编写获取货币交换率的代码。

Engine里面的方法会在ViewController里面调用。一个好的设计实践就是确保你的Engine类不会暴露URL和http的请求头给调用的类。你的View层不应该知道url路径以及须要的參数,就是相应着YahooEngine里面的货币及以及货币单位。

返回的数据能够为double值表示货币交换率或者时间戳。由于请求都是异步的。你应该在block里面返回这些值,比方: 

-(MKNetworkOperation*) currencyRateFor:(NSString*) sourceCurrency
                           inCurrency:(NSString*) targetCurrency
              onCompletion:(CurrencyResponseBlock) completion
                           one rror:(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: 处理返回数据 
处理数据,就是转换你从server取回的数据,不论时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__]

你写的方法应该依照例如以下顺序运行: 

  1. 准备好url和请求參数
  2. 创建一个请求的MKNetworkOperation对象.
  3. 设置方法參数
  4. 加入完毕和错误处理方法,(完毕方法就是你处理你响应到对应的model类的地方)
  5. 可选的。还有请求操作的进度指示。(或者在viewController里面处理)
  6. 假设你的操作时下载文件,那么设置一个下载的流(一般是一个文件)给他,这也是可选的
  7. 当请求操作完毕时,处理结果而且调用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;[/pre]

 
上面的代码构造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: 
上传图片到server(比方TwitPic) 
如今我们看一个怎样上传图片到server的样例,上传图片明显须要操作把data编码为表格请求。MKNetworkKit遵循一系列的类似ASIHttpRequest的请求。 
你能够调用addFile:forKey:在MKNetworkOperation里面来加入文件附件作为请求表格的数据。

非常easy。

 
MKNetworkOperation 也有一个简单的方法来从NSData指针中加入图片。就是调用addData:forKey: 方法来直接从NSData上传图片。

(比方直接从相机加入图片). 

样例 3: 
下载文件到本地文件夹(缓存) 

用MKNetworkKit从远程server下载文件而且保存到用户的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 提供了一些例如以下便捷方法来方便你格式化你的响应数据 

  1. responseData
  2. responseString
  3. responseJSON (Only on iOS 5)
  4. responseImage
  5. responseXML
  6. error
从网络请求获取响应非常方便,当返回格式错误时,这些方法返回nil。比方试图从响应为html中获取image会返回nil,唯一能够确保返回正确结果的方法时responseData,假设你确定返回类型。能够用其它方法。 

方便宏定义 
宏定义Dlog和ALog是我不知羞耻的从stackoverflow上偷的,并且找不到源文件了,假设时你写的。请让我知道 
关于GCD 
我有益没用gcd,由于网络请求须要随时停止和安排优先级,gcd打给你然比NSOperationQueue更高效。可是不能做到上面两点。

我不推荐在网络请求的队列中使用gcd。 
关于文档 
头文件都加了凝视,并且我正在尝试整理。于此同事,你能够随时玩弄代码。 
源码 
源码和Demoproject都在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 



   

iOS Framework: Introducing MKNetworkKit