今天學(xué)習(xí)了一下YTKNetwork,在此寫下心得,記錄學(xué)習(xí)歷程
首先YTKNetwork的基本功能如下:
1,YTKNetwork包含兩個類,分別是YTKNetworkConfig類和YTKRequest類,其中YTKNetworkConfig類是用于統(tǒng)一設(shè)置網(wǎng)絡(luò)請求的服務(wù)器和CDN的地址。YTKRequest類是所有的網(wǎng)絡(luò)請求類需要繼承于YTKRequest類,每一個YTKRequest類的子類代表一種專門的網(wǎng)絡(luò)請求。
其次YTKNetworkConfig類有兩個作用:1)統(tǒng)一設(shè)置網(wǎng)絡(luò)請求的服務(wù)器和CDN地址。2)管理網(wǎng)絡(luò)請求的YTKUrlFilterProtocol實例。
需要統(tǒng)一設(shè)置服務(wù)器地址是因為:1)要按照設(shè)計模式里的應(yīng)該把服務(wù)器地址統(tǒng)一寫在一個地方。2)在實際業(yè)務(wù)中,我們的測試人員需要切換不同的服務(wù)器地址來測試。統(tǒng)一設(shè)置服務(wù)器地址到?YTKNetworkConfig?類中,也便于我們統(tǒng)一切換服務(wù)器地址。
在程序剛啟動的回調(diào)中,設(shè)置好YTKNetworkConfig的信息,代碼如下:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary?*)launchOptions
{
YTKNetworkConfig *config = [YTKNetworkConfig?sharedConfig];
config.baseUrl =?@"http://yuantiku.com";
config.cdnUrl =?@"http://fen.bi";
}
設(shè)置好之后,所有的網(wǎng)絡(luò)請求都會默認使用?YTKNetworkConfig?中?baseUrl?參數(shù)指定的地址。
YTKRequest類
每一種請求都需要繼承?YTKRequest?類,通過覆蓋父類的一些方法來構(gòu)造指定的網(wǎng)絡(luò)請求,每一種網(wǎng)絡(luò)請求繼承?YTKRequest?類后,需要用方法覆蓋的方式,來指定網(wǎng)絡(luò)請求的具體信息。
例如我們要向網(wǎng)址?http://www.yuantiku.com/iphone/register?發(fā)送一個?POST?請求,請求參數(shù)是?username?和?password。那么,這個類應(yīng)該如下所示:
#import?"YTKRequest.h"
@interface?RegisterApi?:?YTKRequest
- (id)initWithUsername:(NSString?*)username?password:(NSString?*)password;
@end
// RegisterApi.m
#import?"RegisterApi.h"
@implementation?RegisterApi?{
NSString?*_username;
NSString?*_password;
}
- (id)initWithUsername:(NSString?*)username?password:(NSString?*)password {
self = [super?init];
if?(self) {
_username = username;
_password = password;
}
return?self;
}
- (NSString?*)requestUrl?{
return?@"/iphone/register";
}
- (YTKRequestMethod)requestMethod?{
return?YTKRequestMethodPOST;
}
- (id)requestArgument?{
return?@{
@"username": _username,
@"password": _password
};
}
@end
在上面這個示例中,我們可以看到:
1)我們通過覆蓋?YTKRequest?類的?requestUrl?方法,實現(xiàn)了指定網(wǎng)址信息。并且只需要指定除去域名剩余的網(wǎng)址信息,因為域名信息在?YTKNetworkConfig?中已經(jīng)設(shè)置過了。
2)我們通過覆蓋?YTKRequest?類的?requestMethod?方法,實現(xiàn)了指定?POST?方法來傳遞參數(shù)。
3)我們通過覆蓋?YTKRequest?類的?requestArgument?方法,提供了?POST?的信息。這里面的參數(shù)?username?和?password?如果有一些特殊字符(如中文或空格),也會被自動編碼。
調(diào)用RegisterApi
在構(gòu)造完成?RegisterApi?之后,具體如何使用呢?我們可以在登錄的?ViewController?中,調(diào)用?RegisterApi,并用?block?的方式來取得網(wǎng)絡(luò)請求結(jié)果:
- (void)loginButtonPressed:(id)sender {
NSString?*username = self.UserNameTextField.text;
NSString?*password = self.PasswordTextField.text;
if?(username.length >?0?&& password.length >?0) {
RegisterApi *api = [[RegisterApi?alloc]?initWithUsername:username?password:password];
[api?startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {
//?你可以直接在這里使用?self
NSLog(@"succeed");
}?failure:^(YTKBaseRequest *request) {
//?你可以直接在這里使用?self
NSLog(@"failed");
}];
}
}
注意:可以直接在?block?回調(diào)中使用?self,不用擔(dān)心循環(huán)引用。因為?YTKRequest?會在執(zhí)行完?block?回調(diào)之后,將相應(yīng)的?block?設(shè)置成?nil。從而打破循環(huán)引用。除了?block?的回調(diào)方式外,YTKRequest?也支持?delegate?方式的回調(diào):
- (void)loginButtonPressed:(id)sender {
NSString?*username = self.UserNameTextField.text;
NSString?*password = self.PasswordTextField.text;
if?(username.length >?0?&& password.length >?0) {
RegisterApi *api = [[RegisterApi?alloc]?initWithUsername:username?password:password];
api.delegate = self;
[api?start];
}
}
- (void)requestFinished:(YTKBaseRequest *)request {
NSLog(@"succeed");
}
- (void)requestFailed:(YTKBaseRequest *)request {
NSLog(@"failed");
}
驗證服務(wù)器返回內(nèi)容
有些時候,由于服務(wù)器的?Bug,會造成服務(wù)器返回一些不合法的數(shù)據(jù),如果盲目地信任這些數(shù)據(jù),可能會造成客戶端?Crash。如果加入大量的驗證代碼,又使得編程體力活增加,費時費力。
使用?YTKRequest?的驗證服務(wù)器返回值功能,可以很大程度上節(jié)省驗證代碼的編寫時間。
例如,我們要向網(wǎng)址?http://www.yuantiku.com/iphone/users?發(fā)送一個?GET?請求,請求參數(shù)是?userId?。我們想獲得某一個用戶的信息,包括他的昵稱和等級,我們需要服務(wù)器必須返回昵稱(字符串類型)和等級信息(數(shù)值類型),則可以覆蓋?jsonValidator?方法,實現(xiàn)簡單的驗證。
- (id)jsonValidator {
return?@{
@"nick": [NSString?class],
@"level": [NSNumber?class]
};
}
完整的代碼如下:
// GetUserInfoApi.h
#import?"YTKRequest.h"
@interface?GetUserInfoApi?:?YTKRequest
- (id)initWithUserId:(NSString?*)userId;
@end
// GetUserInfoApi.m
#import?"GetUserInfoApi.h"
@implementation?GetUserInfoApi?{
NSString?*_userId;
}
- (id)initWithUserId:(NSString?*)userId {
self = [super?init];
if?(self) {
_userId = userId;
}
return?self;
}
- (NSString?*)requestUrl?{
return?@"/iphone/users";
}
- (id)requestArgument?{
return?@{?@"id": _userId };
}
- (id)jsonValidator?{
return?@{
@"nick": [NSString?class],
@"level": [NSNumber?class]
};
}
@end