`
wangpengfei360
  • 浏览: 1056423 次
文章分类
社区版块
存档分类
最新评论

ASIHTTPRequest和libxml结合,实现边请求边解析

 
阅读更多

ASIHTTPRequests 是非常强大的 http 异步请求开源框架,libxml 是非常老牌的 C 语言xml函数库。在 http + xml 文件的 javaEE-iPhone 应用中,如何把二者结合起来,实现在异步请求数据的同时,进行xml的同步解析呢?

这涉及到 3 方面的关键知识:

¥ ASIHTTPRequest

这部分的内容可以参考作者另一篇博文《ASIHTTPRequest的使用》。

¥ NSOperation 和 Libxml

这部分内容在作者的一篇博文《使用NSOperation实现异步下载》中也有介绍。

背景知识已经具备,下面让我们继续。

一、准备libxml环境

libxml2 是一个开放源码库,默认情况下iPhone SDK 中已经包括在内。 它是一个基于 C 的 API,所以在使用上比 cocoa 的 NSXML 要麻烦许多(一种类似 c 函数的使用方式),但是该库同时支持 DOM 和 SAX 解析,其解析速度较快,而且占用内存小,是最适合使用在 iphone 上的解析器。 从性能上讲,所有知名的解析器中,TBXML 最快,但在内存占用上,libxml 使用的内存开销是最小的。因此,我们决定使用 libxml 的sax接口。

首先,我们需要在 project 中导入 framework:libxml2.dylib。

虽然 libxml 是 sdk 中自带的,但它的头文件却未放在默认的地方,因此还需要我们设置 project 的 build 选项:HEADER_SEARCH_PATHS = /usr/include/libxml2,否则 libxml 库不可用。

然后,我们就可以在源代码中 #import <libxml/tree.h> 了。

至于 ASIHTTPRequest 的使用环境,请参考 ASIHTTPRequest的使用》 进行。

二、线程管理

首先,我们肯定要使用线程来进行实现。多线程的操作使用NSOperation子类。

新建 o-c class,命名为 SyncRequestParseOperation ,它必需继承NSOperation。

我们决定不使用继承而使用聚合来让它同时具有 ASIHTTPRequest 和 Xml 解析的功能,因此我们导入了libxml/tree.h 和 ASIHTTPRequest.h 。

由于服务器使用了GBK 编码,所以我们也使用了 NSStringEncoding kRequestStatus 定义了一个枚举,用来表示 SyncRequestParseOperation 的不同状态:请求完毕、请求失败、收到数据包。这3种可能状态会被成员变量 status 使用,实际上它是个int。头文件定义如下:

#import <libxml/tree.h>

#import "BaseXmlParser.h"

#import "ASIHTTPRequest.h"

enum kRequestStatus {

kRequestStatusFinished ,

kRequestStatusFailed ,

kRequestStatusDataReceived

};

@interface SyncRequestParseOperation : NSOperation

{

NSURL * _url ;

NSDictionary * _data ;

// 构建 gb2312 encoding

NSStringEncoding enc ;

//Xml 解析器指针

xmlParserCtxtPtr _parserContext ;

BaseXmlParser * baseParser ;

id delegate , progressDelegate ;

int status ;

}

@property ( nonatomic , retain ) NSDictionary *data;

@property ( nonatomic , retain ) NSURL *url;

@property ( assign ) int status;

- ( id )initWithURLString:( NSString *) url xmlParser:( BaseXmlParser *) parser delegate:( id )obj;

-( void )setProgressDelegate:( id )progress;

-( void )statusChangedNotify;

@end

BaseXmlParser 是一个Xml解析器的基类,我们使用它的子类来进行Xml解析,在其中定义了一些使用 libxml 时特有的结构体和函数声明。有了它,我们就可以在其子类中覆盖某些方法来解析各种不同的XML 文件。

BaseXmlParser 及其子类我们后面会介绍。

delegate和 progressDelegate 保存两个对象的 id 引用。前者是负责响应 SyncRequestParseOperation 类的一些特殊的通知消息,比如某个状态的改变;后者负责根据收到的数据实时进行进度显示。

接下来我们看实现,首先是初始化 init 方法:

initWithURLString :xmlParser: delegate:( id )obj 方法是个便利的初始化方法,分别对3个成员进行初始化,而不必要对它们一一调用setter方法:http请求地址url、解析器、通知消息的委托对象。

- ( id )initWithURLString:( NSString *) url xmlParser:( BaseXmlParser *)parser delegate:( id )obj{

if ( self = [ super init ]) {

_url =[[ NSURL alloc ] initWithString : url ];

delegate =obj;

baseParser =[parser retain ];

// 构建 gb2312 encoding

enc = CFStringConvertEncodingToNSStringEncoding ( kCFStringEncodingGB_18030_2000 );

}

return self ;

}

除了init方法外,我们也提供了 setProgressDelegate 方法:

-( void )setProgressDelegate:( id )progress{

progressDelegate =progress;

}

用于对 progressDelegate 进行初始化。

接下来是最主要的部分,NSOperation的生命周期方法:

#pragma mark NSOperation 的生命周期方法

// 开始线程 - 本类的主方法

- ( void )start {

NSLog ( @"operation start!" );

if (![ self isCancelled ]) {

// 创建 XML 解析器指针

_parserContext = xmlCreatePushParserCtxt (& _saxHandlerStruct , baseParser , NULL , 0 , NULL );

// 以异步方式处理事件,并设置代理块

__block ASIHTTPRequest *request = [ ASIHTTPRequest requestWithURL : _url ];

// 设置进度代理

if ( progressDelegate != nil ) {

[request setDownloadProgressDelegate : progressDelegate ];

}

// 使用 complete 块,在下载完时做一些事情

[request setCompletionBlock :^( void ){

[ self setStatus : kRequestStatusFinished ];

NSLog ( @"request completed!" );

// 添加解析数据(结束),注意最后一个参数 terminate

xmlParseChunk ( _parserContext , NULL , 0 , 1 );

// 添加解析数据(结束),

if ( baseParser != nil ){

[ self setData :[[ baseParser getResult ] copy ]];

} else {

NSLog ( @"baseparser is nil" );

}

// 释放 XML 解析器

if ( _parserContext ) {

xmlFreeParserCtxt ( _parserContext ), _parserContext = NULL ;

}

[ self statusChangedNotify ];

}];

// 使用 failed 块,在下载失败时做一些事情

[request setFailedBlock :^( void ){

[ self setStatus : kRequestStatusFailed ];

NSLog ( @"request failed !" );

// 释放 XML 解析器指针

if ( _parserContext ) {

xmlFreeParserCtxt ( _parserContext ), _parserContext = NULL ;

}

[ self statusChangedNotify ];

}];

// 使用 received 块,在接受到数据时做一些事情

[request setDataReceivedBlock :^( NSData * data ){

[ self setStatus : kRequestStatusDataReceived ];

NSLog ( @"received data:%d" , data . length );

// 添加解析数据(结束),注意最后一个参数 terminate

if ( baseParser != nil && baseParser != NULL ){

[ self setData :[[ baseParser getResult ] copy ]];

} else {

NSLog ( @"baseparser is nil" );

}

// 使用 libxml 解析器进行 xml 解析

xmlParseChunk ( _parserContext , ( const char *)[ data bytes], [ data length ], 0 );

[ self statusChangedNotify ];

}];

[request startAsynchronous ];

}

}

// 停止线程

- ( void )cancel

{

[ super cancel ];

}

对于一个NSOperation 来说,最主要的是start 方法,因为线程在这里启动。由于使用了 ASIHTTPRequest 的异步方式,所以在start方法中我们没有使用 NSRunLoop 循环( 这个问题参考 http://www.cocoabuilder.com/archive/cocoa/279826-nsurlrequest-and-nsoperationqueue.html )。因为 ASIHTTPRequest 的startAsynchronous 方法提供了额外的线程。我们在 start 方法中使用了一个ASIHTTPRequest ,利用 BaseXmlParser 解析器来提供一系列符合 libxml 规范的回调函数,以响应 sax 解析事件。当然,由于我们要实现“边接收数据,边解析Xml”的目的,我们在 ASIHTTPRequest 的三个委托块中,就对数据进行了处理(使用 libxml 的函数)。

比较怪异的是对 ASIHTTPRequest 的3个事件委托中使用了块语法,块语法介绍可以参考作者另一篇(翻译)博文《块编程指南》。

为了把3个委托事件通知给delegate,我们需要在3个事件委托块中调用delegate 的相应方法:

// status 状态变化通知

-( void )statusChangedNotify{

if ( delegate != nil ) {

SEL sel= NSSelectorFromString ( @"syncRequestParseStatusNofity:" );

if ([ delegate respondsToSelector :sel]){

[ delegate performSelector :sel withObject : self ]; // 注意冒号说明带 1 个参数

}

}

}

为了简便,我没有定义新的协议,而只是使用方法名 syncRequestParseStatusNofity: 作为内部协议。如果delegate要想接收通知,就必需实现该方法。作为一种技巧,其中使用了反射机制,避免运行时错误。

三、Sax 异步解析

libxml 是C函数库,其中很多函数需要使用令人生畏的结构体定义。为了便于扩展,这些定义被放到了 BaseXmlParser 类中:

#import <Foundation/Foundation.h>

#import <libxml/tree.h>

@interface BaseXmlParser : NSObject {

NSStringEncoding enc ;

NSMutableDictionary * _root ;

}

// Property

- ( void )startElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix

URI:( const xmlChar *)URI

nb_namespaces:( int )nb_namespaces

namespaces:( const xmlChar **)namespaces

nb_attributes:( int )nb_attributes

nb_defaulted:( int )nb_defaultedslo

attributes:( const xmlChar **)attributes;

- ( void )endElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix URI:( const xmlChar *)URI;

- ( void )charactersFound:( const xmlChar *)ch

len:( int )len;

-( NSDictionary *)getAtributes:( const xmlChar **)attributes withSize:( int )nb_attributes;

-( NSDictionary *)getResult;

@end

//3 个静态方法的实现,其实是调用了 ctx 的成员方法,其中 ctx _parserContext 初始化时传入

static void startElementHandler(

void * ctx,

const xmlChar * localname,

const xmlChar * prefix,

const xmlChar * URI,

int nb_namespaces,

const xmlChar ** namespaces,

int nb_attributes,

int nb_defaulted,

const xmlChar ** attributes)

{

[( BaseXmlParser *)ctx

startElementLocalName :localname

prefix :prefix URI :URI

nb_namespaces :nb_namespaces

namespaces :namespaces

nb_attributes :nb_attributes

nb_defaulted :nb_defaulted

attributes :attributes];

}

static void endElementHandler(

void * ctx,

const xmlChar * localname,

const xmlChar * prefix,

const xmlChar * URI)

{

[( BaseXmlParser *)ctx

endElementLocalName :localname

prefix :prefix

URI :URI];

}

static void charactersFoundHandler(

void * ctx,

const xmlChar * ch,

int len)

{

[( BaseXmlParser *)ctx

charactersFound :ch len :len];

}

//libxml xmlSAXHandler 结构体定义,凡是要实现的 handler 函数都写在这里,不准备实现的用 null 代替。一般而言,我们只实现其中 3 个就够了

static xmlSAXHandler _saxHandlerStruct = {

NULL , /* internalSubset */

NULL , /* isStandalone */

NULL , /* hasInternalSubset */

NULL , /* hasExternalSubset */

NULL , /* resolveEntity */

NULL , /* getEntity */

NULL , /* entityDecl */

NULL , /* notationDecl */

NULL , /* attributeDecl */

NULL , /* elementDecl */

NULL , /* unparsedEntityDecl */

NULL , /* setDocumentLocator */

NULL , /* startDocument */

NULL , /* endDocument */

NULL , /* startElement*/

NULL , /* endElement */

NULL , /* reference */

charactersFoundHandler, /* characters */

NULL , /* ignorableWhitespace */

NULL , /* processingInstruction */

NULL , /* comment */

NULL , /* warning */

NULL , /* error */

NULL , /* fatalError //: unused error() get all the errors */

NULL , /* getParameterEntity */

NULL , /* cdataBlock */

NULL , /* externalSubset */

XML_SAX2_MAGIC , /* initialized 特殊常量,照写 */

NULL , /* private */

startElementHandler, /* startElementNs */

endElementHandler, /* endElementNs */

NULL , /* serror */

};

BaseXmlParser 类的头文件中,可以分为两部分。

1. 第一部分是 interface 定义,定义了BaseXmlParser类的成员,包括:

¥ 成员变量

enc:基于和前面同样的原因,用于定义GBK编码。

_root:一个Dictionary,用于保存解析后Xml对象,一个xml文档只有一个root 元素,因此用一个Dictionary对象即可。

¥ 成员方法

libxml 回调方法:前3个很像是C语言函数的方法其实都是被libxml回调的,它们会在3个静态函数(在第二部分)中调用。

getAttributes方法:这个是一个方便的获取 xml 元素属性的方法。由于本例中的 XML 文档大量使用了属性,所以这个方法很实用。

getResult方法:用于获得 XML 文档解析结果,即 _root 对象。

2. 第二部分是 libxml 回调函数和结构体定义,包括:

¥ 回调函数

本例我们决定实现3个回调函数,分别用于响应 Sax 解析中的3个事件:

处理 XML 元素开始标记、处理 XML 元素结束标记、处理 XML 元素体。

为了更 OO 一些,我们没有直接在这 3 个函数中写对应的 XML 解析代码,而是调用了类的成员方法进行处理。这样,我们可以在 implement 部分写入具体的代码。

¥ 结构体

只需要填充一个结构体 xmlSAXHandler 即可。这个结构成员数量众多(31个),但我们只需填充你要实现的几个。例如,我们要实现3个回调函数,那么只消在对应的地方填充这3个函数名即可(此外有一个特殊的成员叫 XML_SAX2_MAGIC ,你照填就是了)。为了便于大家理解这些成员所代表的意义,我们也在旁边做了注释,你可以对照着看。

接下来是implement (实现)。

#import "BaseXmlParser.h"

@implementation BaseXmlParser

// Property

-( id )init{

if ( self =[ super init ]){

// 构建 gb2312 encoding

enc = CFStringConvertEncodingToNSStringEncoding ( kCFStringEncodingGB_18030_2000 );

_root =[[ NSMutableDictionary alloc ] init ];

}

return self ;

}

-( void )dealloc{

[ _root release ], _root = nil ;

[ super dealloc ];

}

// 一个便利方法,用于获取元素的属性值

-( NSDictionary *)getAtributes:( const xmlChar **)attributes withSize:( int )nb_attributes{

NSMutableDictionary * atts=[[ NSMutableDictionary alloc ] init ];

NSString *key,*val;

for ( int i= 0 ; i<nb_attributes; i++){

key = [ NSString stringWithCString :( const char *)attributes[ 0 ] encoding : NSUTF8StringEncoding ];

val = [[ NSString alloc ] initWithBytes :( const void *)attributes[ 3 ] length :(attributes[ 4 ] - attributes[ 3 ]) encoding : NSUTF8StringEncoding ];

[atts setObject :val forKey :key];

[key release ],[val release ];

attributes += 5 ; // 指针移动 5 个字符串,到下一个属性

}

return atts;

}

//--------------------------------------------------------------//

#pragma mark -- libxml handler ,主要是 3 个回调方法 , 空方法,等待子类实现 --

//--------------------------------------------------------------//

// 解析元素开始标记时触发 , 在这里取元素的属性值

- ( void )startElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix

URI:( const xmlChar *)URI

nb_namespaces:( int )nb_namespaces

namespaces:( const xmlChar **)namespaces

nb_attributes:( int )nb_attributes

nb_defaulted:( int )nb_defaultedslo

attributes:( const xmlChar **)attributes

{

}

// 解析元素结束标记时触发

- ( void )endElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix URI:( const xmlChar *)URI

{

}

// 解析元素体时触发

- ( void )charactersFound:( const xmlChar *)ch

len:( int )len

{

}

// 返回解析结果

-( NSDictionary *)getResult{

return _root ;

}

@end

可以看到,除了 getAttributes 和 getResult 方法外,我们都没有进行其它方法的实现。这是因为 Sax 解析跟 Dom 解析不同,针对不同的 XML 文档很难使用相同的逻辑解析,因此我们准备把剩下的内容留给子类来实现,这样不同的XML 文档可以通过不同的子类来进行解析,而不用在每个子类中都写一遍那些怪异的 C 回调函数和结构体声明。

我们要解析的 XML 文档可能是这样的:

<root>

<List Name="同事">

<user name="t2" phone="13884831140"/>

<user name="t3" phone="15877103548"/>

<user name="t1" phone="13399459990"/>

</List>

<List Name="好友">

<user name="f2" phone="13828831140"/>

<user name="f3" phone="15886103548"/>

<user name="f1" phone="13019459990"/>

</List>

</root>

也就是说,这是一个通讯录类似的东西。通讯录把电话号码按性质分成不同的组,就像Windows mobile智能手机上的的通讯录,把电话号码按“家庭”、“好友”、“同事”等进行划分。

我们新建一个 BaseXmlParser的子类 TelNoXmlParser ,让这个 TelNoXmlParser 去实现 3 个回调方法:

#import <Foundation/Foundation.h>

#import <libxml/tree.h>

#import "BaseXmlParser.h"

@interface TelNoXmlParser : BaseXmlParser {

BOOL loginSuccess ;

NSMutableArray * groups ,* members ;

NSMutableDictionary * _group ;

NSDictionary * _user ;

}

@end

#import "TelNoXmlParser.h"

@implementation TelNoXmlParser

-( id )init{

if ( self =[ super init ]) {

// 一个 groups 数组,代表了所有 List

groups =[[ NSMutableArray alloc ] init ];

[ _root setObject : groups forKey : @"items" ];

loginSuccess = YES ;

}

return self ;

}

-( void )dealloc{

[ _group release ], _group = nil ;

[ super dealloc ];

}

//--------------------------------------------------------------//

#pragma mark -- libxml handler ,主要是 3 个回调方法 --

//--------------------------------------------------------------//

// 解析元素开始标记时触发 , 在这里取元素的属性值以及设置标志变量

- ( void )startElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix

URI:( const xmlChar *)URI

nb_namespaces:( int )nb_namespaces

namespaces:( const xmlChar **)namespaces

nb_attributes:( int )nb_attributes

nb_defaulted:( int )nb_defaultedslo

attributes:( const xmlChar **)attributes

{ // 我们关心 8 个元素标签,所以设置了 8 个标志位

// login_status

if ( strncmp (( char *)localname, "login_status" , sizeof ( "login_status" )) == 0 ) {

loginSuccess = NO ;

return ;

}

if ( loginSuccess ) {

// List

if ( strncmp (( char *)localname, "List" , sizeof ( "List" )) == 0 ) {

NSDictionary * atts=[ self getAtributes :attributes withSize :nb_attributes]; // 获取 List 的所有属性

_group =[[ NSMutableDictionary alloc ] init ];

members =[[ NSMutableArray alloc ] init ];

[ _group setObject : members forKey : @"members" ];

[ _group setObject :[[ NSString alloc ] initWithString :( NSString *)[atts objectForKey : @"Name" ]] forKey : @"groupname" ];

[ groups addObject : _group ]; // group 加入数组

return ;

}

// user

if ( strncmp (( char *)localname, "user" , sizeof ( "user" )) == 0 ) {

NSDictionary * atts=[ self getAtributes :attributes withSize :nb_attributes]; // 获取 List 的所有属性

_user =[[ NSDictionary alloc ] initWithDictionary :atts];

[ members addObject : _user ];

return ;

}

}

}

// 解析元素结束标记时触发

- ( void )endElementLocalName:( const xmlChar *)localname

prefix:( const xmlChar *)prefix URI:( const xmlChar *)URI

{

if ( strncmp (( char *)localname, "root" , sizeof ( "root" )) == 0 ){ //root 结束时置 login_status 标志

if ( loginSuccess ) {

[ _root setObject : @"true" forKey : @"login_status" ];

} else {

[ _root setObject : @"false" forKey : @"login_status" ];

}

}

if ( loginSuccess ) {

// 我们还关心 <List> 的结束标记

if ( strncmp (( char *)localname, "List" , sizeof ( "List" )) == 0 ) {

[ _group release ], _group = nil ; // 回收 _group 对象,以便重复利用

} else if ( strncmp (( char *)localname, "user" , sizeof ( "user" )) == 0 ){

[ _user release ], _user = nil ; // 回收 _user 对象,以便重复利用

}

}

}

// 解析元素体时触发

- ( void )charactersFound:( const xmlChar *)ch

len:( int )len

{

// 没有元素体需要关心

}

@end

接下来我们看如何在 ViewController 中使用。

四、在 UI 中测试

ViewController 中放入一个按钮和一个 WebView,当点击按钮时,请求http服务器,获取通讯录XML 数据,并解析为 Dictionary 对象。把解析结果显示在 WebView 中。

这是按钮的 touch up inside 事件代码:

-( IBAction )go{

if ( _queue == nil ){

_queue = [[ NSOperationQueue alloc ] init ];

}

[ button setEnabled : NO ];

[ progress setProgress : 0 ];

[ webView loadHTMLString : @"" baseURL :[ NSURL URLWithString : URL ]];

// 构造 xmlparser

TelNoXmlParser * parser=[[ TelNoXmlParser alloc ] init ];

// self 注册为 delegate ,这样 self 必需实现 syncRequestParseStatusNofity: 方法 , 以接收 statusChanged 方法

SyncRequestParseOperation * operation=[[ SyncRequestParseOperation alloc ]

initWithURLString : URL

xmlParser :parser

delegate : self ];

// progress 设置为 progressDelegate ,这样会显示进度

[operation setProgressDelegate : progress ];

[parser release ]; // opertaion retain ,可以 release

[ _queue addOperation :operation]; // 开始处理

[operation release ]; // 队列已 retain ,可以 release

}

这是异步消息到达时的处理代码,当数据接收完时,我们把解析结果在WebView 中显示:

// 实现 statusChanged 通知方法

-( void )syncRequestParseStatusNofity:( id )sender{

SyncRequestParseOperation * operation=( SyncRequestParseOperation *)sender;

int status=[operation status ];

NSLog ( @"status:%d" ,status);

if (status== kRequestStatusFinished ){ // 如数据接收完成

[ button setEnabled : YES ];

NSDictionary * d=[operation data ];

[ webView loadHTMLString :[d description ] baseURL :[ NSURL URLWithString : URL ]];

}

}

这是程序运行时 WebView 显示效果:

注意,当xml 文档比较大时,WebView 的内容是从上到下逐渐刷新的。

这是控制台输出,可以看到服务器响应的数据是被分成很多次下载的:


分享到:
评论

相关推荐

    ASIHTTPRequestTest.zip

    "ASIHTTPRequest和libxml结合,实现边请求边解析 "一文源代码

    ASIHTTPRequest+UITableView实现多个下载任务

    ASIHTTPRequest+UITableView实现多个下载任务,没用到重用机制,还有没有实现断点续载,很简单的一个demo,相信初学者都能看懂,还写了一些注释。

    ASIHttpRequest

    利用ASIHttpRequest实现客户端向服务器端请求登陆验证的示例 博客参考:http://blog.csdn.net/dingxiaowei2013/article/details/12617203

    iOS ASIHttpRequest 请求https

    iOS ASIHttpRequest 请求https

    ASIHTTPRequest

    ASIHTTPRequest,用于获取下载及其相关处理与应用的功能函数

    ASIHTTPRequest网路请求

    ASIHTTPRequest网络请求集合,直接引入到项目中使用。

    ASIHttpRequest 队列下载 UITableView实现

    使用ASI开源库,实现队列下载。使用UITableView进行展示

    ASIHttpRequest网络请求工具

    ASIHttpRequest是iOS开发必备的网络数据请求包,使用方便,唯一的缺点是非ARC的,需要设置项目中的非ARC类

    ASIHttpRequest网络请求框架

    全称是ASIHTTPRequest,外号“HTTP终结者”,可以实现http网络请求,功能十分强大。

    详解iOS – ASIHTTPRequest 网络请求

    可以很好的应用在 Mac OS X 系统和 iOS 平台的应用程序中,ASIHTTPRequest 适用于基本的 HTTP 请求,和基于 REST 的服务之间的交互。可惜作者早已停止更新,有一些潜在的 BUG 无人去解决,很多公司的旧项目里面都...

    取消同步的ASIHTTPRequest请求

    检查ASIHTTPRequest类的startSynchronous方法,注意下面这段代码, if (![self isCancelled] && ![self complete]) { [self main]; while (!complete) { [[NSRunLoop currentRunLoop] runMode:[self ...

    ASIHTTPRequest断点续传

    ASIHTTPRequest实现资源的下载,断点续传

    IOS ASIHttpRequest资源包

    ASIHTTPRequest是简单易用的,它封装了CFNetwork API。使得与Web服务器通信变得更简单。它是用Objective-C编写的,可以在MAC OS X和iPhone应用中使用。...ASIFormDataRequest子类可以简单的实现提交数据和文件。

    ASIHttpRequest ios开发框架

    ios开发框架 ASIHttpRequest 资源来源于网上 非原创

    ASIHTTPRequest 最新版本 包 下载

    使用iOS SDK中的HTTP网络请求API,相当的复杂,调用很繁琐,ASIHTTPRequest就是一个对CFNetwork API进行了封装,并且使用起来非常简单的一套API,用Objective-C编写,可以很好的应用在Mac OS X系统和iOS平台的应用...

    asihttprequest带demo代码包

    asihttprequest是目前做移动平台游戏上比较便捷的http通信第三方库

Global site tag (gtag.js) - Google Analytics