概述git
現在大多App都會與網絡打交道,做爲開發者,合理的對網絡後臺請求接口進行封裝十分重要。本文要介紹的就是一種常見的採用回調函數(方法)的網絡接口封裝,也算的是一種構架吧。github
這個構架主要的idea是這樣的,把全部的接口封裝成一個類,在工程中隨時能夠調用。而且利用代理Delegate構建回調方法(callBack),工程中隨處能夠經過回調方法監聽網絡請求的反饋,也就是說,一旦獲得了服務器反饋的數據,回調函數中的代碼就(才)會被激活。網絡請求基於AFNetworking(AFNetworking,很是有名的網絡請求第三方類庫),請求均爲異步。如此構架,很是靈活很容易擴展和複用。數據庫
講解json
要想使用本文介紹的構架,你首先須要掌握代理(Delegate),若是你不熟悉代理,這個構架對你來講將會很不解。對於不熟悉代理的同窗們,建議大家去看一下資料。網絡請求其實說白了就是和服務器作一個數據交互,App把請求數據發給服務器,服務器返回給App一個反饋數據。請先看一下這個構架的示意圖,以下:api
如上圖,這個構架的主要節點有三個,封裝網絡請求的類(接口類)、使用網絡請求的類(圖中的ViewController)、和服務器。服務器
Ok~故事是這樣的,一個夜黑風高的...醉醺醺ViewController走在湖邊,爲了找回被關在雲端的Data,他苦練數載終於參透了《接口類》,天地無情,今天是時候作個了斷了。網絡
因而乎他從懷中拿出了傳說中的「接口類」,使用內力,實例化了一個接口類的對象,接着口中念出「接口類實例.delegate=self」,拔出利劍在身旁實現了「接口類」中的一個代理方法。而後調用接口的方法,方法經過內嵌的AFNetworking,向服務器發出了一道請求。又是一陣夜風吹過,三兩枯葉瑟瑟落下。ViewController酣意漸濃閉上了眼睛,如今他能作的惟有等待...架構
鏡頭一轉,月色中,在天上,在雲端的服務器,ViewController剛纔發出的請求正在興風做浪,雲端值夜班的衆神絲絕不敢怠慢各個大步流星,從數據庫中搜索着能化解這道請求的神器。異步
此時,ViewController睏意漸濃,眼皮似墜了千金重物,意識也漸漸模糊。忽然雲端顯出異像,ViewController頓時醒了過來,隱約能夠看到,雲端有數據絲絲縷縷的流動,而本身懷中的「接口類」內嵌的AFNetworking也變得熾熱起來,HTTP反饋block像是要爆炸同樣的顫動着。ViewController豆大的汗珠從額頭滾下,不再能淡定,口中叨咕着,快了,快來了... 一個霹靂,剛纔用劍實現的代理方法金光一閃刺得ViewController捂住了雙眼。ide
一切都回歸安靜後,ViewController睜開眼睛,發現Data安靜的躺在代理方法的裏面...
代碼示例
下面經過一個例子,來介紹一下。
打開Xcode我建了一個SingleViewApp,而後把AFNetworking加載進工程,以下圖:
咱們 OpenWeatherMap提供的天氣預報的API做爲例子,簡單地利用上述構架,作一個天氣預報的App
咱們來看一下這個接口怎麼用,很簡單:
例子:http://api.openweathermap.org/data/2.5/weather?q=beijing
參數:q=城市名字
返回Json:
{"coord":{"lon":116.4,"lat":39.91},"sys":{"type":1,"id":7405,"message":0.013,"country":"CN","sunrise":1435870233,"sunset":1435924003},"weather":[{"id":800,"main":"Clear","description":"Sky is Clear","icon":"01d"}],"base":"stations","main":{"temp":305.43,"pressure":1008,"humidity":28,"temp_min":302.15,"temp_max":308.71},"visibility":10000,"wind":{"speed":2,"deg":0},"clouds":{"all":0},"dt":1435900364,"id":1816670,"name":"Beijing","cod":200}
爲了簡單咱們的Demo App就只顯示 天氣和溫度,UI以下圖:
簡單直觀,點擊不一樣城市名字命名的按鈕,在Label中顯示其天氣情況,關於UI不是今天討論的重點,咱們主要討論網絡和接口。
如今開始重頭戲:接口類
新建一個類我把它命名爲「Net」類,繼承NSObject,並導入"AFNetworking.h"頭文件:
// // Net.h // NetInterface // // Created by Oliver on 15/7/3. // #import <Foundation/Foundation.h> #import "AFNetworking.h" @interface Net : NSObject @end
這個類就是咱們一直提到的接口類,咱們要吧全部的網絡接口都寫到這個類裏面。如今寫一個天氣預報接口做爲例子。爲天氣預報接口在Net類裏聲明一個實例方法,因爲這個接口須要傳得參數只有一個城市名稱,在Net類的H文件因此方法聲明以下:
/** * 得到某城市的天氣 * * @param cityName 城市名稱 */ -(void)getWeatherInfoWithCity:(NSString *)cityName;
一塊兒看起來都很美好對不對?那麼如今我要提一點,可能會被你們忽略的因素。因爲咱們實際開發的App調用接口的次數可能會不少,並且調用接口的類也不少,因此,Net這個類將會被屢次的實例化,那麼頗有可能App的網絡層會變得很亂更有甚者會出Bug。因此,像這樣的接口類,咱們有必要將它作成單例的,整個App共享一個接口類的實例。Ok,下面就來介紹獲取單例的方法:
在H文件聲明獲取單例的方法:
/** * 獲取Net類的單例 * * @return Net類的單例 實例(對象) */ +(Net *)getInstance;
接下來咱們在Net.m文件實現獲取單例方法:(由於全部的接口請求都是HPPT請求,會用到AFNetworking的AFHTTPRequestOperationManager,因此我在getInstace方法裏面把Manager也單例了)
#import "Net.h" __strong static AFHTTPRequestOperationManager *AFHTTPMgr; __strong static Net *NetInstance=nil; @implementation Net +(Net *)getInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken,^{ NetInstance= [[Net alloc]init];//初始化實例 //一下是AFHTTPOerrationManager的配置 AFHTTPMgr=[AFHTTPRequestOperationManager manager]; //申明返回的結果是json類型 AFHTTPMgr.responseSerializer=[AFJSONResponseSerializer serializer]; //申明請求的數據是json類型 AFHTTPMgr.requestSerializer=[AFJSONRequestSerializer serializer]; //若是報接受類型不一致請替換一致text/xml或別的 //AFHTTPMgr.responseSerializer.acceptableContentTypes= [NSSet setWithObject:@"text/xml"]; //設置超時時間 AFHTTPMgr.requestSerializer.timeoutInterval=5; }); return NetInstance; } @end
上面代碼中,由於很變量的操做是在Block中作的,而block中不能對block外的變量進行從新更改,因此在程序的實現以前,聲明瞭:
__strong static AFHTTPRequestOperationManager *AFHTTPMgr;
__strong static Net *NetInstance=nil;
以便在單例的Block裏面對其進行更改。
接下啦,咱們就能夠繼續去實現接口的方法getWeatherInfoWithCity:
-(void)getWeatherInfoWithCity:(NSString *)cityName{ //接口地址 NSString *url=[NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather"]; //參數 NSDictionary *parameters=[[NSDictionary alloc]initWithObjectsAndKeys:cityName,@"q", nil]; //發請求 [AFHTTPMgr GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { //請求成功Block } failure:^(AFHTTPRequestOperation *operation, NSError *error) { //請求失敗Blick }]; }
如上代碼所示,這就是咱們獲取天氣預報的接口,AFNetworking的請求成功和請求失敗的回調Block咱們暫且空着,由於,咱們要設置了Delegate再用。爲何咱們要用代理而不是直接把想作的事情放在AFNetworking的Block裏面呢?
答案其實顯而易見,Block是輕量級的代碼塊,雖然使用簡單,可是很是的封閉,與外部(Block外)進行數據交換的能力很是的有限。好比咱們天氣預報的例子,咱們的ViewController類但願經過服務器返回的天氣信息,改變UILabel的信息,而這個數據又在Net這個類的Block裏面,沒辦法傳遞給ViewController,這就讓局面變得很是尷尬。因此咱們要使用代理Delegate。其實Delegate的核心的做用就是來實現類之間的數據傳遞。如今請你,再次看一下上面的那張架構示意圖,我想你會對其有更深的理解。
下面,聲明Net類的代理,H文件的代碼以下:
在導入頭文件聲明和@interface之間 用@protocol聲明代理
// Net.h // NetInterface #import <Foundation/Foundation.h> #import "AFNetworking.h" //代理 @protocol NetDelegate <NSObject> /** * 代理回調方法 * * @param feedbackInfo 服務器返回的數據 */ -(void)getWeatherInfoSuccessFeedback:(id)feedbackInfo; -(void)getWeatherInfoFailFeedback:(id)failInfo; @end @interface Net : NSObject @property (nonatomic,strong) id<NetDelegate> delegate; /** * 獲取Net類的單例 * * @return Net類的單例 實例(對象) */ +(Net *)getInstance; /** * 得到某城市的天氣 * * @param cityName 城市名稱 */ -(void)getWeatherInfoWithCity:(NSString *)cityName; @end
如上代碼,這是Net類的完整地H文件,咱們在代理部分,聲明瞭兩個方法,一個請求成功、一個請求失敗。在代理中申明的代理方法,咱們不用去實現它,而是在M文件總直接使用它。若是本身要使用的代理咱們須要將代理聲明爲本身的成員變量:
@property (nonatomic,strong) id<NetDelegate> delegate;
OK,如今讓咱們回到getWeatherInfoWithCirt:方法,在Block中使用代理方法。代碼以下:
-(void)getWeatherInfoWithCity:(NSString *)cityName{ //接口地址 NSString *url=[NSString stringWithFormat:@"http://api.openweathermap.org/data/2.5/weather"]; //參數 NSDictionary *parameters=[[NSDictionary alloc]initWithObjectsAndKeys:cityName,@"q", nil]; //發請求 [AFHTTPMgr GET:url parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) { //請求成功Block //將返回數據傳入代理方法 [self.delegate getWeatherInfoSuccessFeedback:responseObject]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { //請求失敗Blick //將錯誤信息傳入代理方法 [self.delegate getWeatherInfoFailFeedback:error]; }]; }
OK, 若是你一路跟下來,恭喜你,你的方法類構建完成了。你的每個接口均可以按照以上的方式,寫成接口類的方法,而後用代理把它傳遞給其餘類。
那麼其餘類怎麼接受經過代理傳遞過來的數據呢?
打開ViewController,導入「Net.h」文件,在繼承聲明後添加實現<NetDelegate>代理,以下代碼:
// ViewController.h // NetInterface #import <UIKit/UIKit.h> #import "Net.h" @interface ViewController : UIViewController <NetDelegate> @end
爲了使用方便我添加了一個Net類的成員變量,KYNet:
@property Net *KYNet;
接下來咱們要在M文件中使用接口嘍~~~代碼以下:
// // ViewController.m // NetInterface #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _KYNet=[Net getInstance];//獲得單例
_KYNet.delegate=self; //將KYNet的代理與ViewController鏈接
} //北京按鈕 - (IBAction)beijingTouched:(id)sender { [_KYNet getWeatherInfoWithCity:@"Beijing"]; } //上海按鈕 - (IBAction)shanghaiTouched:(id)sender { [_KYNet getWeatherInfoWithCity:@"Shanghai"]; } @end
如上代碼,當咱們按下按鈕,就會使用咱們的接口類發送請求
慢着~怎麼接收服務器反饋數據?!
對了,下面咱們經過實現Net的代理方法來接受處理數據,並更新到UILabel上,在M文件實現,Delegate的兩個方法:
-(void)getWeatherInfoSuccessFeedback:(id)feedbackInfo{ //當服務器返回成功數據後,下列代碼被激活 NSLog(@"%@",[feedbackInfo class]); NSDictionary *dic=feedbackInfo; NSArray *weather1=[dic objectForKey:@"weather"]; NSDictionary *main1=[dic objectForKey:@"main"]; NSDictionary *weather=[weather1 objectAtIndex:0]; NSString *temp=[NSString stringWithFormat:@"%@",[main1 objectForKey:@"temp"]]; NSString *weatherInfo=[NSString stringWithFormat:@"%@",[weather objectForKey:@"description"]]; _condition.text=weatherInfo; _tem.text=temp; } -(void)getWeatherInfoFailFeedback:(id)failInfo{ NSLog(@"%@",failInfo); }
完活~
Hit Run~~~
總結
手指頭敲酸了...寫博客比寫代碼累多啦TT。Ok總結一下。
本文的核心思想是把全部的網路請求封裝成一個類,向外部提供各個接口的請求方法,以便使用者發送請求;而當服務器返回反饋數據後,外部經過實現代理方法來得到數據。這樣的架構的好處是很是靈活,低耦合,擴展簡單。實現的代理方法會在服務器返回數據的是時候自動被調用,結合異步的AFNetworking,開發者不用去擔憂線程問題。這樣一來,程序主線的邏輯設計也會變得很簡單。用此構架封裝好的類,能夠輕鬆的打包成SDK給別人使用。
謝謝你們,但願大家有所收穫,一篇文章花了我整整一天時間,若是對你有所幫助請幫忙點贊。若有問題,歡迎評論。若要轉載,請註明出處。