同步請求和異步請求:json
同步請求會阻塞主線程;不會開啓新的線程,仍是主線程,因此直到請求成功後,才能執行其它操做。api
異步請求不會阻塞主線程。開啓新的線程去請求服務器,而不影響用戶的交互操做等其餘動做。緩存
使用NSURLConnection發送同步請求和異步請求:性能優化
同步請求:服務器
異步請求:(block回調方式)——請求的數據經過block返回網絡
異步請求:(delegate方式)——請求的數據在重寫的代理方法裏返回app
須要注意的是:框架
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
didReceiveData的參數data是從服務器返回的二進制數據,因此咱們使用NSMutableData類型來接收。所以,json解析的核心就是把從網絡而來的二進制數據(NSData)轉換爲json對象。而後從json結構獲取有意義的數據。
NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
補充:網絡請求返回的數據通常都是二進制數據(NSData),但若請求是純文檔,則也可使用NSString接收。異步
// // ViewController.m // JSONTest // // Created by mac on 15-3-30. // Copyright (c) 2015年 ___FULLUSERNAME___. All rights reserved. // #import "ViewController.h" #import "App.h" #import "UIImageView+WebCache.h" #define kSearchUrl @"https://api.douban.com/v2/book/search?q=s" @interface ViewController ()<UITableViewDataSource,UITableViewDelegate,NSURLConnectionDataDelegate,NSURLConnectionDelegate> { NSMutableArray * appArr; NSMutableData * downloadData; UITableView * tabView; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; downloadData = [NSMutableData data]; appArr = [NSMutableArray array]; //新建tableView [self createTableView]; //創建異步請求 [self makeConnection]; } - (void)createTableView { tabView = [[UITableView alloc]initWithFrame:CGRectMake(0, 0, 320, 480) style:UITableViewStyleGrouped]; tabView.delegate = self; tabView.dataSource = self; tabView.rowHeight = 80; [self.view addSubview:tabView]; // [tabView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } - (void)makeConnection { [NSURLConnection connectionWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:kSearchUrl]] delegate:self]; } - (void)decodeJson:(NSMutableData *)data { NSError * error; NSDictionary * jsonDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]; NSArray * jsonArr = jsonDict[@"books"]; for(NSDictionary * dict1 in jsonArr) { NSString * large = dict1[@"images"][@"large"]; NSString * title = dict1[@"title"]; NSString * author = dict1[@"author"][0]; App * app = [[App alloc]init]; app.large = large; app.title = title; app.author = author; [appArr addObject:app]; } NSLog(@"%d",appArr.count); } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return appArr.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"cell"]; if(cell==nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"]; if(appArr.count>0) { App * app = appArr[indexPath.row]; cell.textLabel.text = app.title; cell.detailTextLabel.text = app.author; [cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.large] placeholderImage:[UIImage imageNamed:@"photo"]]; } } return cell; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { downloadData.length = 0; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [downloadData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { //解析json數據 [self decodeJson:downloadData]; [tabView reloadData]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"%@",error); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end
使用第三方網絡請求框架AFNetworking:性能
先實例化出一個manager對象,對網絡的GET和POST請求都是經過它的方法完成的。
success的block返回值responseObject就是服務器傳過來的完整數據。返回的數據類型默認是json格式的。它的強大不只在於高度封裝了對網絡的請求,並且能夠根據不一樣情形設置請求數據和返回數據的格式。它默認的請求數據類型爲二進制的,默認的返回數據類型爲json。若請求的服務器是json文檔,則返回的直接就是json數據,直接獲取有意義的數據就行;若請求的服務器是xml文檔,則先要使其返回數據類型爲xml格式的。
能夠在success的block裏完成數據解析。
AFHTTPRequestOperationManager * manager = [AFHTTPRequestOperationManager manager]; [manager GET:kSearchUrl parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { NSLog(@"請求成功"); //responseObject就是傳過來的數據,並且是一次性完整的數據。能夠在這裏完成數據解析工做 } failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"請求失敗"); }];
補充:
manager 的POST請求有兩個方法,上面是下載,還有一個用於上傳。
參數formData就是要上傳的數據。
圖片緩存問題:(性能優化)
上面的代碼用到了一個很是優秀的第三方框架SDWebImage。它會自動實現異步加載,圖片緩存,控制同一URL加載次數(同一個url不會重複請求)。
[cell.imageView sd_setImageWithURL:[NSURL URLWithString:app.large] placeholderImage:[UIImage imageNamed:@"photo"]];
該句就是核心代碼,它自動會幫咱們實現圖片緩存。並且在第一次去服務器加載圖片還沒獲取到時,是用佔位圖片的(placeholderImage),等從服務器加載到了圖片後就被替換掉了
設想,要是在加載圖片的時候咱們不使用SDWebImage框架,那連咱們滑動tableView時,只要滑出現一次就會去服務器加載一次,這無疑是很糟糕的,太費流量和時間了。此時咱們的策略是使用圖片緩存,首先判斷有沒有緩存,如有緩存,則用緩存的圖片;若無緩存,纔去服務器加載。緩存則分爲內存緩存和本地緩存。內存緩存是不理想的,由於它比較佔用內存,影響APP運行速度。其次內存緩存只存在於APP一個運行週期裏,當關閉APP時,內存緩存會清空,下次打開APP時,又得去服務器加載圖片。面對這樣的問題,咱們一般都採用本地緩存,也就是第一次從服務器把圖片加載後,寫入本地文件,那之後再次打開時,只要讀取本地文件的圖片就能夠了,這樣減小了請求服務器的次數,既下降了流量的消耗,又提高了性能。