简介
在本文中,我将给大家介绍ios中如何运用NSURLConnection从网络上下载数据,以及解析JSON数据格式的数据,还有数据的显示和图片异步下载。
涉及到的知识点:
1.NSURLConnection的异步下载和数据请求方法的封装。
2.认识JSON格式和JSON格式的解析使用
3.数据在模拟器上的显示和图片的异步下载(使用SDWebImage异步显示图片,SDWebImage是一个库)
注意:
在ios开发中,无论是数据还是图片都是使用异步下载方法,不能使用同步。
内容
首先,要完成一个项目,你得需要素材,图片,还有网络接口(网络接口其实就一下载数据的网址URL,因为你的app数据是通过网络从服务端请求过来的)
1.网络下载基础知识介绍
(1)手机app网络应用
玩过智能手机我们都知道,手机里的app有本地应用和网络应用,说通俗点本地应用就是不需要网络也能使用,而一般微信,QQ等需要上网才能使用的就是网络应用,这些网络应用是需要网上下载数据才能运行的。这里我们只讲网络应用。
(2)网络应用的结构程序
分两种,一种是客户端,一种是服务端。需要上网下载数据才能使用的网络应用就是客户端,而为这个网络应用提供数据服务的就是服务端。
(3)常见的网络接口模式
ios网络应用的常见接口一半都是HTTP形式的URL地址,
例如爱限免应用首页的数据地址为http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id=
在项目中一般使用一些开源库(开源库的时候往后再讲)通过这种网址下载数据. 例如AFNetworking
(4)常见数据格式
iOS开发中常见的数据格式有两种, 一种是JSON格式, 另外种是XML格式, 相对来说, JSON格式使用的比较多
(5)app界面开发的一般流程
ios中开发一个界面,需要界面效果图,界面素材资源,网络接口这三项
开发的流程一般如下:
***1.确定你要开发的步骤,一般可以先获取数据,即从网络接口中下载数据
***2.然后把你下载的数据(JSON或XML数据)进行解析,创建数据模型model,一般用MVC这样的设计模式来编程开发
***3.使用UI控件显示这些解析后的数据,一般都需要自定义cell显示
2.具体实现步骤
1.使用异步下载数据的方法 (NSURLConnection异步下载)把这个方法封装起来,以后可以代码复用
***1.先说普通简单状态下的异步下载,网络接口以这个为例子
http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id=
[self testNSURLConnectionAsyncDownloadData]; //JSON语法 [self jsonFormat]; } -(void)jsonFormat { //JSON //JavaScript Object Notation /* { "count":20, "data":[ "zhangsan", "lisi", "wangwu" ] } */ //[] 表示数组,对应NSArray //, 表示并列的数据 //{} 表示字典,对应NSDictionary //: 表示键值对 //"ta" 表示字符串,对应NSString //20 对应NSNumber //JSON格式格式化工具 // Jason // Json Editor // 在线: http://www.kjson.com/ } #pragma mark - 异步下载 -(void)testNSURLConnectionAsyncDownloadData { NSString *urlString = @"http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id="; //初始化 _data = [[NSMutableData alloc] init]; //发起了一个异步的URL连接请求 //异步: 执行了方法之后开始下载,立即返回 // 下载过程在后台(多线程)执行 _connection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]] delegate:self startImmediately:YES]; NSLog(@"initWithRequest 执行完成"); }
当使用这个方法的时候,下载的过程就在程序后台进行,遵循 NSURLConnectionDataDelegate 这个协议就能调用相关方法了,比如下载完成后执行的方法,从而完成相关操作
***2.下面就是相关方法操作:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ NSLog(@"接收到那个方法initWithRequest,服务器响应执行"); } //接收到数据就执行这个方法,下载数据 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ [_data appendData:data]; } //数据下载完成后使用的方法,直接看英文意思 -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ NSString *str = [[NSString alloc]initWithData:_data encoding:NSUTF8StringEncoding]; NSLog(@"打印下载后的数据%@",str); NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:_data options:NSJSONReadingMutableContainers error:nil]; NSLog(@"%@",dic); NSArray *arr = dic[@"applications"]; for (NSDictionary *dic2 in arr) { NSLog(@"name = %@",dic2[@"name"]); } }
2.通过上面那个简单的例子,我们认识了NSURLConnection异步下载,接下来就介绍下异步下载的封装
***1.创建一个继承NSObject的类
***2.定义一个保存数据的属性和处理这些数据的实例方法
#import <Foundation/Foundation.h> @interface ZJHttpRequest : NSObject //保存下载的数据 @property (copy,nonatomic) NSMutableData *data; //作用:传入网址,下载完成后执行target对象中action方法 类比按钮,点击执行事件 -(void)requestWithUrl:(NSString *)url target:(id)target action:(SEL)action; @end
***3..m文件的实现步骤
#import "ZJHttpRequest.h" //消除performSelector的警告 #pragma clang diagnostic ignored "-Warc-performSelector-leaks" //类扩展 //项目实践: // 有些实例变量内部使用, 不想放在头文件中, 放在这儿 @interface ZJHttpRequest ()<NSURLConnectionDataDelegate> { NSURLConnection *_connection; NSString *_url; id _target; SEL _action; } @end @implementation ZJHttpRequest //作用: // 传入网址, 下载完成执行后执行target对象中action方法 -(void)requestWithUrl:(NSString *)url target:(id)target action:(SEL)action { _url = url; _target = target; _action = action; //发起URL请求 _data = [[NSMutableData alloc] init]; _connection = [[NSURLConnection alloc] initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self startImmediately:YES]; } -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_data appendData:data]; } -(void)connectionDidFinishLoading:(NSURLConnection *)connection { //下载完成了, 执行保存的方法 if(_target && [_target respondsToSelector:_action]) { [_target performSelector:_action withObject:self]; } } @end
***4.如果不能很好的理解其中每段代码代表是意思,可以看看下面我的注释,如果有错,非常欢迎各位看官的指教
#import "ZJHttpRequest.h" //消除performSelector的警告 #pragma clang diagnostic ignored "-Warc-performSelector-leaks" //类扩展 //项目实践: //有些实例变量内部使用,不想放在头文件中,放在这@interface @end之间 @interface ZJHttpRequest ()<NSURLConnectionDataDelegate>{ NSURLConnection *_connection; NSString *_url; id _target; SEL _action; } @end @implementation ZJHttpRequest -(void)requestWithUrl:(NSString *)url target:(id)target action:(SEL)action{ _url = url;//把传过来的参数网址URL,赋给新创建的 NSString *_url对象 _target = target;//传过来的参数target target QFPXViewController * 0x8cea970 0x08cea970 _action = action;//传过来的参数action SEL "dealDowloadFinish:" 0x00009a4f _data = [[NSMutableData alloc]init]; //NSURLConnection异步下载,执行了这个方法就在后台下载了 _connection = [[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]] delegate:self startImmediately:YES];//_connection NSURLConnection * 0x8d79720 0x08d79720 记住,这个是异步下载,当程序执行了这个方法后,下载数据就变成在后台下载了,(多线程),所以之后requestWithUrl之后就已经在下载数据,然后创建表格,表格则遵循协议,调用那两个方法,返回数据行数,而在NSURLConnection异步下载中,也需要遵循协议,就会有你下载了数据后可以执行这个方法didReceiveData //程序走向就是,当在QFPXViewController,执行这个封装的方法的时候,会把相应的参数传过来,或者说,在QFPXViewController,调用了封装的方法,就跑到这个封装的方法中 --requestWithUrl, //然后,创建表格,而后获取数据,return _dataArray.count 后就到了下面这里didReceiveData, } -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{ //data __NSCFData * 17088 bytes 0x08d8e140 17088字节 [_data appendData:data]; //appendData 附加数据 //_data NSConcreteMutableData * 17088 bytes 0x08d67980 _data也得到了这么多字节 } -(void)connectionDidFinishLoading:(NSURLConnection *)connection{ //接收完数据了,已经下载完成,然后判断传过来的target和action这两个参数有没有赋给_target和_action,或者说_target和_action是不是空的,一个保障手段方法,如果传过来了,就符合if判断语句,则执行里面的方法,也就是程序的逻辑思维里,那边QFPXViewController传过来两个参数,然后在这里封装的类ZJHttpRequest里用异步下载,下载完数据后判断,是否传了参数过来,如果不为空的话就响应执行 //_action带过来的方法dealDowloadFinish:,也就是再跳到那个类写的方法里,处理数据,这就是封装了方法的一个作用,其实就是异步请求下载数据,下载后再来个参数判断,至于下载后的数据处理还是“仿佛“在QFPXViewController中处理,其实只不过是在QFPXViewController中写处理程序逻辑而已 //self ZJHttpRequest * 0x8c593c0 0x08c593c0 //_target QFPXViewController * 0x8ce52b0 0x08ce52b0 //_action SEL "dealDowloadFinish:" 0x00009a4f //_target是否为空并且_target能否响应_action if (_target && [_target respondsToSelector:_action]) { //判断传过来参数了则执行这里面带过来的方法,执行了带过来的方法dealDowloadFinish,则跳到这个方法里 [_target performSelector:_action withObject:self];//这段代码的意思就是让这个类QFPXViewController 执行perform这个选择的方法_action(dealDowloadFinish)用这个类self带的参数 } } @end
这样,我们就把这个异步下载的方法封装好了,下次就直接调用就可以了,引入头文件,创建该头文件对象,调用封装的实例方法,传过去参数,执行,并返回执行带过去处理数据的方法。
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //数据接口 NSString *urlString = @"http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id="; _dataArray = [[NSMutableArray alloc] init]; //下载 _request = [[ZJHttpRequest alloc] init]; [_request requestWithUrl:urlString target:self action:@selector(dealDowloadFinish:)]; [self createTableView]; }
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //数据接口 网络接口 NSString *urlStr = @"http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id="; _dataArray = [[NSMutableArray alloc]init]; //下载 _request = [[ZJHttpRequest alloc]init]; //self self QFPXViewController * 0x8cea970 0x08cea970 //urlStr __NSCFConstantString * @"http://iappfree.candou.com:8080/free/applications/limited?currency=rmb&page=1&category_id=" 0x000106d4 //调用这个ZJHttpRequest类对象的请求方法,方法requestWithUrl已经是类ZJHttpRequest封装好的,所有,你在这里self QFPXViewController *调用的话就是把当前的target,action这两个参数还有网址URL传入self ZJHttpRequest这个类中 [_request requestWithUrl:urlStr target:self action:@selector(dealDowloadFinish:)]; [self createTabelView]; }
下面是处理数据的方法dealDowloadFinish
//这个方法都相当于放在viewWillAppear里面了,因为程序的走向是先下面这里的方法,再给表返回return _dataArray.count;所以刷新数据就在这里刷新 -(void)dealDowloadFinish:(ZJHttpRequest *)request{ NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:request.data options:NSJSONReadingMutableContainers error:nil]; // NSLog(@"dic = %@",dic); NSArray *appList = dic[@"applications"]; for (NSDictionary *dic in appList) { AppModel *model = [[AppModel alloc]init]; model.applicationId = dic[@"applicationId"]; model.name = dic[@"name"]; model.iconUrl = dic[@"iconUrl"]; [_dataArray addObject:model]; } [_tabelView reloadData]; }
3.来到这里,就顺势说到JSON数据格式的解析
1.相信解析过JSON数据格式的朋友都知道,就有个Jason软件非常好用,使用也很简单
***1.通过网络接口得到原始数据,然后全选中这些数据,黏贴到
com+v然后就回弹出这么个窗口
接着我们点击左上角的Text就得到我们想要的格式化好后的JSON数据了
4.接下来就是认识学习JSON数据格式,这样就能完成以后的数据解析操作
[self jsonFormat]; } -(void)jsonFormat { //JSON //JavaScript Object Notation /* { "count":20, "data":[ "zhangsan", "lisi", "wangwu" ] } */ //[] 表示数组,对应NSArray //, 表示并列的数据 //{} 表示字典,对应NSDictionary //: 表示键值对 //"ta" 表示字符串,对应NSString //20 对应NSNumber //JSON格式格式化工具 // Jason // Json Editor // 在线: http://www.kjson.com/ }
5.数据获取,解析完后就用自定义cell来显示
1.用的石MVC程序设计模式来实现,创建一个基础NSObject的数据类和基础于UITableViewCell的自定义的cell,以下是部分代码
[self createTabelView]; } -(void)createTabelView{ _tabelView = [[UITableView alloc]initWithFrame:self.view.bounds style:UITableViewStylePlain]; _tabelView.dataSource = self; _tabelView.delegate = self; [self.view addSubview:_tabelView]; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ // return _dataArray.count;后返回高度,返回了_dataArray.count次 return 80; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _dataArray.count; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *identifer = @"cell"; //数据有10行,但是这里的判断显示是创建了7个cell,显示6个 AppCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer]; if (cell == nil) { cell = [[AppCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifer]; } AppModel *model = _dataArray[indexPath.row]; //cell.textLabel.text = model.name; cell.nameLabel.text = model.name; [cell.iconImageView setImageWithURL:[NSURL URLWithString:model.iconUrl]]; return cell; }
6.完成效果图
7.图片的下载实现用的是SDWebImage这个库里的方法
1.选择工程,点Build Phases,搜索SDW,得到所有sd开头的文件
2.全选择,靠右边双击弹出一个框,输入-fno-objc-arc,使用这个模式
3.然后再在实现的类下引入头文件#import "UIImageView+WebCache.h"
4.最后,用这个方法实现图片的加载显示
[cell.iconImageView setImageWithURL:[NSURL URLWithString:model.iconUrl]];
总结:实现一个项目工程,简单的界面显示
一.拿到界面素材,网络接口
二.获取数据,解析数据
三.显示数据
其中NSURLConnection异步下载和方法封装,以及JSON数据的解析都是重点,实现的关键