NSURLSession與AFNetworking3.0
下面是用GET方式請求一個頁面數據的示例:
AFNetworking 2.x
NSString *siteUrl = @"http://webinar.ofweek.com/readDemoFile.action"; NSDictionary *parameters = @{@"activity.id":@"9866033",@"user.id":@"2"}; AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; manager.responseSerializer.acceptableContentTypes = [NSSet setWithObject:@"text/html"]; // manager.responseSerializer.acceptableContentTypes = [manager.responseSerializer.acceptableContentTypes setByAddingObject:@"text/html; charset=GBK"]; [manager GET:siteUrl parameters:parameters success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) { NSLog(@"responseString:%@",operation.responseString); NSData *responseData = operation.responseData; NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSString *strResponseData = [[NSString alloc] initWithData:responseData encoding:enc]; NSLog(@"responseData:%@",strResponseData); NSLog(@"responseObject:%@",responseObject); } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) { NSLog(@"failed,%@",error); } ];
AFNetworking 3.x
NSString *siteUrl = @"https://www.shopbop.com/actions/viewSearchResultsAction.action"; NSDictionary *parameters = @{@"query":@"bag",@"baseIndex":@"80"}; AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; manager.responseSerializer = [AFHTTPResponseSerializer serializer]; [manager GET:siteUrl parameters:parameters success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"success,%@",task.response.MIMEType); NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; NSLog(@"%@", responseString); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"failed"); } ];
iOS9中引入了新特性App Transport Security (ATS),該特性要求App內訪問網站必須使用HTTPS協議。下面的方法簡單的關閉了這個限制:
1. 在Info.plist中添加新項NSAppTransportSecurity,類型為Dictionary。
2. 在NSAppTransportSecurity下添加子項NSAllowsArbitraryLoads,類型為Boolean,值設為YES。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

NSURLSession
NSURLSession是iOS7新推出的網絡接口,它與以前的NSURLConnection是并列的。如果用戶強制關閉應用,NSURLSesssion會斷掉。它支持三種類型的任務:加載數據,下載和上傳。
利用NSURLSession進行數據傳輸需要以下流程:
創建一個NSURLSessionConfiguration,用于接下來創建NSSession時需要的工作模式
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
工作模式分為:
默認會話模式(default) — 工作模式類似于原來的NSURLConnection,使用的是基于磁盤緩存的持久化策略,使用用戶keychain中保存的證書進行認證授權。
瞬時會話模式(ephemeral) — 該模式不適用磁盤保存任何數據。所有和會話相關的caches,證書,cookies等都被保存在RAM中,因此當程序使會話無效,這些緩存的數據就會被自動清空。
后臺會話模式(background) — 該模式在后臺完成上傳和下載,在創建configuration對象的時候需要提供一個NSString類型的ID用于標識完成工作的后臺會話。
NSURLConnection實際上是由一系列組件組成,包括有:NSURLRequest、NSURLResponse、NSURLProtocol、NSURLCache、NSHTTPCookieStorage、NSURLCredentialStorage以及同名的NSURLConnection。
在WWDC2013中,Apple的開發團隊對NSURLConnection進行了重構,并推出了NSURLSession作為替代。NSURLSession的大部分組件與NSURLConnection中的組件相同,不同在處在于它將NSURLConnection替換為NSURLSession和NSURLSessionConfiguration,以及三個NSURLSessionTask的子類:NSURLSessionDataTask、NSURLSessionUploadTask和NSURLSessionDownloadTask。
下面是NSURLSession新推出的類:
NSURLSessionConfiguration類
可以通過該類配置會話的工作模式,三種模式的代碼如下:
+ (NSURLSessionConfiguration *)defaultSessionConfiguration; + (NSURLSessionConfiguration *)ephemeralSessionConfiguration; + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
在backgroundSessionConfiguration:方法中的identifier參數指定了會話ID,用于標記后臺的session。
該類還有兩個重要的屬性:
/* allow request to route over cellular. */ @property BOOL allowsCellularAccess; /* allows background tasks to be scheduled at the discretion of the system for optimal performance. */ @property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);
allowsCellularAccess屬性指定是否允許使用蜂窩連接,discretionary屬性為YES時表示當程序在后臺運作時由系統自己選擇最佳的網絡連接配置,在使用后臺傳輸數據的時候,建議使用discretionary屬性而不是allowsCellularAccess屬性,因為discretionary屬性會綜合考慮WiFi可用性和電池電量。(當設備有足夠電量時,設備才通過WiFi進行數據傳輸。如果電量低,或者只打開了蜂窩連接,傳輸任務將不會執行)
NSURLSession類
獲取該類的實例有下面幾種方式:
/* * The shared session uses the currently set global NSURLCache, * NSHTTPCookieStorage and NSURLCredentialStorage objects. */ + (NSURLSession *)sharedSession; /* * Customization of NSURLSession occurs during creation of a new session. * If you only need to use the convenience routines with custom * configuration options it is not necessary to specify a delegate. * If you do specify a delegate, the delegate will be retained until after * the delegate has been sent the URLSession:didBecomeInvalidWithError: message. */ + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration; + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
第一種方式是使用靜態的sharedSession方法,將返回共享的會話,該會話使用全局的Cache、Cookie和證書。
第二種方式是通過sessionWithConfiguration:方法根據NSURLSessionConfiguration的值創建對應工作模式下的會話。
第三種方式是通過sessionWithConfiguration:delegate:delegateQueue方法創建對象,在第二種方式的基礎上增加了session的委托和委托所處的隊列。當不再需要連接時,可以調用NSURLSession的invalidateAndCancel方法直接關閉,或者調用finishTasksAndInvalidate等待當前Task結束后關閉。這兩種關閉方法都會觸發delegate類的URLSession:didBecomeInvalidWithError:事件。
NSURLSessionTask類
這是一個抽象類,它包含三個子類:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。這三個類封裝了獲取數據,上傳和下載任務。下面是繼承關系圖:

(1)NSURLSessionDataTask
利用NSURLRequest對象或NSURL對象來創建該類的實例:
/* Creates a data task with the given request. The request may have a body stream. */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request; /* Creates a data task to retrieve the contents of the given URL. */ - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
利用NSURLRequest對象或NSURL對象來創建該類的實例,當任務完成后,通過completionHandler指定回調的代碼塊:
/* * data task convenience methods. These methods create tasks that * bypass the normal delegate calls for response and data delivery, * and provide a simple cancelable asynchronous interface to receiving * data. Errors will be returned in the NSURLErrorDomain, * see <Foundation/NSURLError.h>. The delegate, if any, will still be * called for authentication challenges. */ - (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; - (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
讀取數據示例:
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressCircle; @property (weak, nonatomic) IBOutlet UIWebView *webView; - (IBAction)loadButtonClicked:(id)sender; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)loadButtonClicked:(id)sender { // 開始加載數據,開始轉動 [self.progressCircle startAnimating]; NSURL *url = [NSURL URLWithString:@"http://tech.qq.com/it.htm"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler: ^(NSData *data, NSURLResponse *response, NSError *error) { //打印出返回的狀態碼,請求成功返回200 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; NSInteger responseStatusCode = [httpResponse statusCode]; NSLog(@"%ld", (long)responseStatusCode); //打印出返回的代碼 NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000); NSString *strResponseData = [[NSString alloc] initWithData:data encoding:enc]; NSLog(@"ResponseData String:%@",strResponseData); // 在UIWebView中加載數據 [self.webView loadData:data MIMEType:@"text/html" textEncodingName :@"utf-8" baseURL:[NSURL URLWithString:@"http://tech.qq.com"]]; //加載數據完畢,停止轉動 if ([NSThread isMainThread]) { [self.progressCircle stopAnimating]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ [self.progressCircle stopAnimating]; }); } }]; //使用resume方法啟動任務 [dataTask resume]; } @end
(2)NSURLSessionUploadTask
利用NSURLRequest對象創建該類的實例,在上傳時指定文件源或數據源
/* Creates an upload task with the given request. The body of the request will be created from the file referenced by fileURL */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; /* Creates an upload task with the given request. The body of the request is provided from the bodyData. */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData; /* Creates an upload task with the given request. The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */ - (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
同上,當任務完成后,通過completionHandler指定回調的代碼塊:
/* * upload convenience method. */ - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler; - (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler;
上傳示例:
#import <UIKit/UIKit.h> @interface ViewController : UIViewController <NSURLSessionDataDelegate> @end #import "ViewController.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (IBAction)testClicked:(id)sender { NSURL *uploadURL = [NSURL URLWithString:@"http://www.synchemical.com/temp/UploadImage4.ashx?param2=iphone6"]; NSString *boundary = @"----------V2ymHFg03ehbqgZCaKO6jy"; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *uploadFilePath = [documentsDirectory stringByAppendingPathComponent:@"ad6.png"]; NSLog(@"file path: %@",uploadFilePath); NSString *fileName = [uploadFilePath lastPathComponent]; NSMutableData *mutableData = [NSMutableData data]; NSData *fileData = [[NSData alloc] initWithContentsOfFile:uploadFilePath]; // NSData *dataOfFile = UIImageJPEGRepresentation(self.imageView.image,1.0); if (fileData) { //Body part for "textContent" parameter. This is a string. NSString *textContent = @"This is a parameter string"; [mutableData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"textContent"] dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:[[NSString stringWithFormat:@"%@\r\n", textContent] dataUsingEncoding:NSUTF8StringEncoding]]; //end [mutableData appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:[[NSString stringWithFormat:@"Content-Disposition:form-data; name=\"myfile\"; filename=\"%@\"\r\n",fileName] dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:[@"Content-Type:application/zip\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:fileData]; [mutableData appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [mutableData appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; } NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:uploadURL]; [request setHTTPMethod:@"POST"]; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",boundary]; [request setValue:contentType forHTTPHeaderField:@"Content-Type"]; NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromData:mutableData completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { NSString *message = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"message:%@", message); [session invalidateAndCancel]; //當不再需要連接時,可以調用Session的invalidateAndCancel直接關閉 }]; [uploadTask resume]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend { NSLog(@"already sent:%lld",bytesSent); NSLog(@"totoal to send:%lld",totalBytesSent); NSLog(@"expected send:%lld",totalBytesExpectedToSend); } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end <%@ WebHandler Language="C#" Class="UploadImage" %> using System; using System.Web; using System.IO; public class UploadImage : IHttpHandler { public void ProcessRequest(HttpContext context) { HttpPostedFile myFile = context.Request.Files["myfile"]; string strFolder = HttpContext.Current.Server.MapPath(context.Request["folder"]); myFile.SaveAs(strFolder +"/uploadFolder/" + myFile.FileName); if (!Directory.Exists(strFolder)) { Directory.CreateDirectory(strFolder); } context.Response.Write("param: "+context.Request["textContent"]); } public bool IsReusable { get { return false; } } }
(3)NSURLSessionDownloadTask
利用NSURLRequest對象或NSURL對象來創建該類的實例:
/* Creates a download task with the given request. */ - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request; /* Creates a download task to download the contents of the given URL. */ - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url; /* Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */ - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
利用NSURLRequest對象或NSURL對象來創建該類的實例,當任務完成后,通過completionHandler指定回調的代碼塊,最后一個方法支持通過之前未下載完成的數據繼續下載
/* * download task convenience methods. When a download successfully * completes, the NSURL will point to a file that must be read or * copied during the invocation of the completion routine. The file * will be removed automatically. */ - (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler; - (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler; - (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler;
下載示例:
#import "ViewController.h" @interface ViewController () <NSURLSessionDelegate> { NSData *partialData; } @property (strong, nonatomic) NSURLSessionDownloadTask *downloadTask; @property (strong, nonatomic) NSURLSession *session; @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *progressCircle; @property (weak, nonatomic) IBOutlet UIWebView *webView; - (IBAction)downloadButtonClicked:(id)sender; @end @implementation ViewController - (IBAction)downloadButtonClicked:(id)sender { [self.progressCircle startAnimating]; NSURL *URL = [NSURL URLWithString:@"http://www.synchemical.com/temp/2800kb.jpg"]; NSURLRequest *request = [NSURLRequest requestWithURL:URL]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[NSOperationQueue mainQueue]]; self.downloadTask = [self.session downloadTaskWithRequest:request]; [self.downloadTask resume]; } - (IBAction)resumeButtonClicked:(id)sender { // 傳入上次暫停下載返回的數據,就可以恢復下載 self.downloadTask = [self.session downloadTaskWithResumeData:partialData]; // 開始任務 [self.downloadTask resume]; // 清空 partialData = nil; } - (IBAction)pauseButtonClicked:(id)sender { [self.downloadTask cancelByProducingResumeData:^(NSData *resumeData) { partialData = resumeData; self.downloadTask = nil; }]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { NSLog(@"=========下載進度========="); // 獲得下載進度 NSLog(@"totalBytesWritten:%lld",totalBytesWritten); NSLog(@"totalBytesExpectedToWrite:%lld",totalBytesExpectedToWrite); NSLog(@"download percent:%f",(double)totalBytesWritten / totalBytesExpectedToWrite); } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"finish download"); //打印出返回的狀態碼,請求成功返回200 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)downloadTask.response; NSInteger responseStatusCode = [httpResponse statusCode]; NSLog(@"hxw: %ld", (long)responseStatusCode); //輸出下載文件原來的存放目錄 NSLog(@"file tmp path: %@", location); //設置文件的存放目錄 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsPath = [paths objectAtIndex:0]; NSURL *documentsURL = [NSURL fileURLWithPath:documentsPath]; NSString *filePath = [[downloadTask.response URL] lastPathComponent]; NSURL *fileURL = [documentsURL URLByAppendingPathComponent:filePath]; NSLog(@"file save path:%@",fileURL.path); //如果路徑下文件已經存在,先將其刪除 NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir; if ([fileManager fileExistsAtPath:fileURL.path isDirectory:&isDir]) { NSLog(@"存在文件"); if (isDir) { NSLog(@"它是個目錄"); } else { NSLog(@"它是個普通文件"); } [fileManager removeItemAtURL:fileURL error:nil]; } //移動文件 BOOL success = [fileManager moveItemAtURL:location toURL:fileURL error:nil]; if (success) { dispatch_async(dispatch_get_main_queue(), ^{ //UIImage *image = [UIImage imageWithContentsOfFile:[fileURL path]]; //self.imageView.image = image; //self.imageView.contentMode = UIViewContentModeScaleAspectFill; //在webView中加載圖片文件 NSURLRequest *showImage_request = [NSURLRequest requestWithURL:fileURL]; [self.webView loadRequest:showImage_request]; //下載完畢,停止轉動 if ([NSThread isMainThread]) { [self.progressCircle stopAnimating]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ [self.progressCircle stopAnimating]; }); } }); } else { NSLog(@"Couldn't copy the downloaded file"); } } - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end

浙公網安備 33010602011771號