在實際工作中,有機會寫網(wǎng)絡(luò)相關(guān)的部分代碼。經(jīng)過討論,大的方向是將iOS傳輸簡化為數(shù)據(jù)業(yè)務(wù)(只實現(xiàn)POST),上傳業(yè)務(wù),下載業(yè)務(wù)三部分。編程語言是Object-C,最低支持版本為iOS7,第三方庫選擇AFNetworking3.0。(如果是新項目,編程語言是Swift,最低支持版本為iOS8,第三方庫選擇Alamofire)
AFNetworking學(xué)習
AFNetworking3.0相比以前的版本精簡了許多,強烈建議升級。
gitHub上的源代碼
官方文檔
整體架構(gòu)

AFURLSessionManager
整個AFNetworking3.0中最核心的一個類。如果想用NSURLSession函數(shù)族自己實現(xiàn)網(wǎng)絡(luò)通訊,可以參考這個類的一些做法。
直接從NSObject繼承而來,并沒有直接繼承NSURLSession。session成為一個屬性,用組合代替繼承,更容易理解。
多線程采用了NSOperationQueue,這是對GCD的一種對象化封裝,使用起來更方便,而且能夠很方便地實現(xiàn)順序依賴,取消等功能。
/**
The managed session.
*/
@property (readonly, nonatomic, strong) NSURLSession *session;
/**
The operation queue on which delegate callbacks are run.
*/
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
- 雖然名字是XXXManager,但是沒有采用單例模式。
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
- 分為數(shù)據(jù)、上傳、下載三種不同的業(yè)務(wù)。
/**
Creates an `NSURLSessionDataTask` with the specified request.
@param request The HTTP request for the request.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionUploadTask` with the specified request for an HTTP body.
@param request The HTTP request for the request.
@param bodyData A data object containing the HTTP body to be uploaded.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request
fromData:(nullable NSData *)bodyData
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
/**
Creates an `NSURLSessionDownloadTask` with the specified request.
@param request The HTTP request for the request.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param destination A block object to be executed in order to determine the destination of the downloaded file. This block takes two arguments, the target path & the server response, and returns the desired file URL of the resulting download. The temporary file used during the download will be automatically deleted after being moved to the returned URL.
@param completionHandler A block to be executed when a task finishes. This block has no return value and takes three arguments: the server response, the path of the downloaded file, and the error describing the network or parsing error that occurred, if any.
@warning If using a background `NSURLSessionConfiguration` on iOS, these blocks will be lost when the app is terminated. Background sessions may prefer to use `-setDownloadTaskDidFinishDownloadingBlock:` to specify the URL for saving the downloaded file, rather than the destination block of this method.
*/
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;
AFHTTPSessionManager
繼承自AFURLSessionManager,專門用來實現(xiàn)HTTPS協(xié)議,提供了POST、GET、HEAD、DELETE、PUT、PATCH等方便方法。
具體的實現(xiàn)都直接或者間接調(diào)用了父類AFURLSessionManager數(shù)據(jù)業(yè)務(wù)的方法,下載和上傳業(yè)務(wù)沒有涉及
/**
Creates an `NSURLSessionDataTask` with the specified request.
@param request The HTTP request for the request.
@param uploadProgressBlock A block object to be executed when the upload progress is updated. Note this block is called on the session queue, not the main queue.
@param downloadProgressBlock A block object to be executed when the download progress is updated. Note this block is called on the session queue, not the main queue.
@param completionHandler A block object to be executed when the task finishes. This block has no return value and takes three arguments: the server response, the response object created by that serializer, and the error that occurred, if any.
*/
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler;
- 雖然名字是XXXManager,也提供了工廠方法
+ (instancetype)manager;,但是并沒有使用單例模式。
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
- 通過包裝,在父類AFURLSessionManager的基礎(chǔ)上隱藏了NSURLRequest的概念,簡化為urlString,并且是相對于baseURL的相對路徑,會在內(nèi)部進行拼接,形成一個完整的urlString。
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
Request和Response
包括AFURLRequestSerialization和AFURLResponseSerialization兩個文件。但是這兩個是protocol,而不是類。真正的主體是AFHTTPRequestSerializer和AFHTTPResponseSerializer。這里的命名沒有采用主體的名字,感覺挺別扭的。
這兩個類用在了AFHTTPSessionManager中,在AFURLSession中只用到AFURLResponseSerialization,但是這兩個類的命名都是AFURLXXX,不是很好。
根據(jù)不同的編碼方式,提供了AFJSONXXX,AFXMLXXX等子類。在默認的實現(xiàn)中,response采用了JSON的方式,而request不是。這也不是很好。
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
- 設(shè)置http頭部信息
/**
Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header.
@param field The HTTP header to set a default value for
@param value The value set as default for the specified header, or `nil`
*/
- (void)setValue:(nullable NSString *)value
forHTTPHeaderField:(NSString *)field;
AFSecurityPolicy
- 分為三個安全等級
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
};
默認是AFSSLPinningModeNone,一般要防止中間人攻擊,可以選用第三種AFSSLPinningModeCertificate。不過客戶端也要放證書文件(.cer),這個要跟后臺統(tǒng)一考慮。
不是單例模式
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;
return securityPolicy;
}
AFNetworkReachabilityManager
- 通過屬性可以知道當前的網(wǎng)絡(luò)狀態(tài)。
/**
Whether or not the network is currently reachable.
*/
@property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable;
/**
Whether or not the network is currently reachable via WWAN.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN;
/**
Whether or not the network is currently reachable via WiFi.
*/
@property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi;
- 通過block來監(jiān)控網(wǎng)絡(luò)狀況??梢源蜷_或者關(guān)閉監(jiān)控功能
/**
Starts monitoring for changes in network reachability status.
*/
- (void)startMonitoring;
/**
Stops monitoring for changes in network reachability status.
*/
- (void)stopMonitoring;
///---------------------------------------------------
/// @name Setting Network Reachability Change Callback
///---------------------------------------------------
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
*/
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
- 默認監(jiān)控本地狀況,已經(jīng)兼容IPv6。
+ (instancetype)manager
{
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 90000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101100)
struct sockaddr_in6 address;
bzero(&address, sizeof(address));
address.sin6_len = sizeof(address);
address.sin6_family = AF_INET6;
#else
struct sockaddr_in address;
bzero(&address, sizeof(address));
address.sin_len = sizeof(address);
address.sin_family = AF_INET;
#endif
return [self managerForAddress:&address];
}
- 采用單例模式
+ (instancetype)sharedManager {
static AFNetworkReachabilityManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [self manager];
});
return _sharedManager;
}
UIKit+AFNetworking
通過類別給UIKit添加網(wǎng)絡(luò)功能,這在當時是比較推薦的做法
隨著APP規(guī)模的增大,現(xiàn)在強調(diào)界面、邏輯、數(shù)據(jù)分層,降低耦合度。所以給UIKit組件添加網(wǎng)絡(luò)功能的做法就不推薦了。
圖片下載和緩存可以用,不過更有名的第三方庫是SDWebImage
其他參考
AFNetworking 概述(一)
AFNetworking 的核心 AFURLSessionManager(二)
處理請求和響應(yīng) AFURLSerialization(三)
AFNetworkReachabilityManager 監(jiān)控網(wǎng)絡(luò)狀態(tài)(四)
驗證 HTTPS 請求的證書(五)
架構(gòu)設(shè)計

ZADataTask
數(shù)據(jù)業(yè)務(wù)
直接調(diào)用AFNetworking的AFHTTPSessionManager實現(xiàn)
參數(shù)只有url,parameter;只用POST方式,放棄GET方式。
request部分也用JSON解析
self.sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
- 安全性再包一層,隱藏AFNetworking的相關(guān)內(nèi)容
typedef NS_ENUM(NSInteger , ZASecurityLevel) {
ZASecurityLevelNormal, // AFSSLPinningModeNone
ZASecurityLevelMiddle, // AFSSLPinningModePublicKey
ZASecurityLevelHigh, // AFSSLPinningModeCertificate
};
ZAUploadTask
上傳業(yè)務(wù)
直接調(diào)用AFNetworking的AFURLSessionManager實現(xiàn)
放棄用POST實現(xiàn)上傳的方式,采用更通用的NSURLSessionUploadTask。
ZADownloadTask
上傳業(yè)務(wù)
直接調(diào)用AFNetworking的AFURLSessionManager實現(xiàn)
ZANetworkConfig
單例
一些可供配置的信息,比如數(shù)據(jù)業(yè)務(wù)的baseUrl
數(shù)據(jù),上傳,下載業(yè)務(wù)提供不同的baseUrl,這三者可以為不同的服務(wù)器
// server base url
@property (nonatomic, copy) NSString *dataTaskBaseUrl;
@property (nonatomic, copy) NSString *uploadTaskBaseUrl;
@property (nonatomic, copy) NSString *downloadTaskBaseUrl;
ZANetworkManager
單例
管理所有的task
// task array
@property (nonatomic, strong) NSMutableArray *dataTaskArray;
@property (nonatomic, strong) NSMutableArray *uploadTaskArray;
@property (nonatomic, strong) NSMutableArray *downloadTaskArray;
ZANetworkReachbility
- 監(jiān)控網(wǎng)絡(luò)狀況,創(chuàng)建本地,數(shù)據(jù)服務(wù)器,上傳服務(wù)器,下載服務(wù)器4個不同的監(jiān)控對象。
// reachability
self.localReachability = [AFNetworkReachabilityManager sharedManager];
self.dataTaskServerReachability = [AFNetworkReachabilityManager managerForDomain:self.config.dataTaskBaseUrl];
self.uploadTaskServerReachability = [AFNetworkReachabilityManager managerForDomain:self.config.uploadTaskBaseUrl];
self.downloadTaskServerReachability = [AFNetworkReachabilityManager managerForDomain:self.config.downloadTaskBaseUrl];
[self.localReachability startMonitoring];
[self.dataTaskServerReachability startMonitoring];
[self.uploadTaskServerReachability startMonitoring];
[self.downloadTaskServerReachability startMonitoring];
- 雖然AFURLSessionManager有屬性reachabilityManager,大致也可以用,不過不是非常好。數(shù)據(jù)、上傳、下載三種業(yè)務(wù)都不是單例,在沒有這些實例存在的情況下,屬性是無效的。所以這里考慮將監(jiān)控網(wǎng)絡(luò)狀況和具體的網(wǎng)絡(luò)業(yè)務(wù)分開,用一個單例來完成網(wǎng)絡(luò)監(jiān)控功能,同時也不用當心實例的生存期問題,同時還可以將本地,數(shù)據(jù)服務(wù)器,上傳服務(wù)器,下載服務(wù)器同時進行監(jiān)控。
過程筆記
用屬性還是成員變量?
用屬性,不要用成員變量,帶_的一般是內(nèi)部變量,不要輕易用。
雖然有時候改錯時XCode提的意見是帶_的成員變量,不過還是建議用屬性。
在init函數(shù)中也照樣用屬性,不要用帶_的成員變量。AFNetworking就是這么做的。
對頭文件中暴露的屬性,readonly的怎么辦(用屬性會報錯)?AFNetworking的對策是在內(nèi)部類擴展中提供一個同名的readwrite屬性。
頭文件AFHTTPSessionManager.h中的只讀聲明:
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
源文件AFHTTPSessionManager.m中的同名私有屬性:
@interface AFHTTPSessionManager ()
@property (readwrite, nonatomic, strong) NSURL *baseURL;
@end
getter和setter一般情況都不要寫,用系統(tǒng)默認的
在getter或者setter函數(shù)中做額外的事情,這種做法雖然可以,但是并不是一個好的習慣。這個時候,應(yīng)該思考,把這項功能當做一個屬性是不是合適?在大多數(shù)情況下,用一個方法來替代這個對外暴露的屬性是更好的選擇。
怎么取消宏定義?
Object-C、C語言開發(fā),#define是很常見的一種做法。不過Swift都不提供宏定義功能,所以考慮不用宏定義
一般來說,都是推薦用const定義的變量來取代宏定義;但是由于作用域問題,遠不如宏定義來得方便。這種方法基本不可取。
一般來說,需要限制全局變量的使用,其副作用甚至比宏定義更大。取代全局變量的一個方法是單例。
同理,用單例來取代宏定義,應(yīng)該是可行的。ZANetworkConstant就是這樣的一個單例,用只讀屬性來保證不被修改。
@interface ZANetworkConstant : NSObject
+ (instancetype)sharedInstance;
// server base url
@property (nonatomic, readonly, copy) NSString *defaulDataTaskBaseUrl;
@property (nonatomic, readonly, copy) NSString *defaulUploadTaskBaseUrl;
@property (nonatomic, readonly, copy) NSString *defaulDownloadTaskBaseUrl;
@end
錯誤信息處理
Object-C編程,一般網(wǎng)絡(luò)錯誤用NSError來表示。一般按照下面的簡單方法使用就好了
- code: 用來放自定義的error code
- domain:自定義的error domain,比如“com.company.app”
- uerInfo:NSLocalizedDescriptionKey對應(yīng)的error message
統(tǒng)一頭文件
將調(diào)用者需要包含的頭文件歸總在一個文件中是一個不錯的方法
AFNetworking, UIKit等都是這么做的
#ifndef ZANetwork_h
#define ZANetwork_h
/**
* 自定義的類型
*/
#import "ZANetworkType.h"
/**
* 公共配置單例
*/
#import "ZANetworkConfig.h"
/**
* 數(shù)據(jù)業(yè)務(wù)
*/
#import "ZADataTask.h"
#endif /* ZANetwork_h */
相對url還是絕對url?
在實際使用中,數(shù)據(jù)業(yè)務(wù)占80%以上的使用場景。一般分為生存,驗收,測試三個不同服務(wù)器,使正式版,試用版,開發(fā)版之間不會相互影響。所以,數(shù)據(jù)業(yè)務(wù)的url一般分為baseUrl和相對url兩部分,方便切換服務(wù)器地址。
上傳和下載的url一般都是通過數(shù)據(jù)業(yè)務(wù),從數(shù)據(jù)服務(wù)器返回的,很多情況下是絕對url。所以對于上傳和下載,并沒有將url分為baseUrl和相對url的需求。
在很多時候,數(shù)據(jù)、上傳、下載三種業(yè)務(wù)共用一個服務(wù)器。切換時,服務(wù)器在拼接上傳、下載業(yè)務(wù)的絕對url時,直接使用數(shù)據(jù)業(yè)務(wù)的baseUrl就可以了。對服務(wù)端編碼來說,也沒有任何變化。
如果上傳、下載業(yè)務(wù)跟數(shù)據(jù)業(yè)務(wù)的服務(wù)器不是同一個,并且也要根據(jù)生產(chǎn)、驗證、測試等場景切換服務(wù)器,那么現(xiàn)在大多數(shù)的url都要改為baseUrl+相對url的形式?,F(xiàn)實中,這個問題只是被掩藏了而已。
AFHTTPSessionManager提供了baseUrl屬性,而AFURLSessionManager沒有提供baseUrl屬性。
AFHTTPSessionManager和AFURLSessionManager初始化函數(shù)都是調(diào)用了NSURLSession的初始化函數(shù)。而NSURLSession初始化是不需要url參數(shù)的。雖然AFHTTPSessionManager的baseUrl參數(shù)在初始化的時候傳入,但是并沒有在初始化的時候使用,只是保存在一個私有屬性中。
AFHTTPSessionManager用到baseUrl的時候是在形成NSURLRequest的時候,將baseUrl和數(shù)據(jù)業(yè)務(wù)函數(shù)傳入的url進行拼接,形成一個總的url,構(gòu)造NSURLRequest,然后使用。各種Method的函數(shù)都是調(diào)用以下函數(shù)實現(xiàn),這個函數(shù)中有拼接url的代碼
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
if (serializationError) {
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
AFURLSessionManager的數(shù)據(jù)、上傳、下載業(yè)務(wù)相關(guān)的函數(shù),都是直接使用NSURLRequest作為參數(shù)的,要求的是絕對url。
為了切換服務(wù)器的靈活性和整體一致性,上傳、下載業(yè)務(wù)也采用baseUrl和相對url組合的方式,進行拼接,形成最終的url,生成NSURLRequest,調(diào)用AFURLSessionManager的相關(guān)函數(shù)實現(xiàn)。
至于上傳、下載服務(wù)器和數(shù)據(jù)業(yè)務(wù)共用一個的情況,只要將他們的baseUrl設(shè)成一樣就可以了。
當然,這就要求服務(wù)在返回上傳、下載服務(wù)的url時,不要拼接,只要返回相對的url就可以了。
如果服務(wù)端一定要返回完整的絕對url,怎么辦呢?一種方法是將上傳、下載的baseUrl設(shè)為空字符串@“”;另一種方法是通過判斷前綴@“http”(兼容HTTPS)識別絕對url,不拼接,直接用就好了。這里采用第二種方法。