1.NSURLSession概述
1. NSURLSession session类型
NSURLSession包括下面3种session类型
a). Default session(默认会话模式):
使用的是基于磁盘缓存的持久化策略,工作模式类似于原来的NSURLConnection,可以用来取代NSURLConnection中的:
[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]
b). Ephemeral session(瞬时会话模式):
临时的进程内会话(内存),不会将cookie、证书、缓存储存到本地,只会放到内存中,当应用程序退出后数据也会消失。
c). Background session(后台会话模式):
和默认会话模式类似, 不过相比默认模式,该会话会在后台开启一个线程进行网络数据处理。
//Default session
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
//Ephemeral
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//Background
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
你需要了解包括NSURLSession、NSURLSessionConfiguration 以及 NSURLSessionTask 的 3 个子类:NSURLSessionDataTask,NSURLSessionUploadTask,NSURLSessionDownloadTask。
与 NSURLConnection 相比,NSURLsession 最直接的改进就是可以配置每个 session 的缓存,协议,cookie,以及证书策略,甚至跨程序共享这些信息。这将允许程序和网络基础框架之间相互独立,不会发生干扰。每个 NSURLSession 对象都由一个 NSURLSessionConfiguration 对象来进行初始化。
NSURLSession 中另一大块就是 session task。它负责处理数据的加载以及文件和数据在客户端与服务端之间的上传和下载。NSURLSessionTask 与 NSURLConnection 最大的相似之处在于它也负责数据的加载,最大的不同之处在于所有的 task 共享其创造者 NSURLSession 这一公共委托者(common delegate)。
2.NSURLSessionTask类
NSURLSessionTask是一个抽象子类,它有三个子类:NSURLSessionDataTask,NSURLSessionUploadTask和NSURLSessionDownloadTask。这三个类封装了现代应用程序的三个基本网络任务:获取数据,比如JSON或XML,以及上传和下载文件。
下面是其继承关系:
不同于直接使用 alloc-init 初始化方法,task 是由一个 NSURLSession 创建的。每个 task 的构造方法都对应有或者没有 completionHandler 这个 block 的两个版本。
1).NSURLSessionDataTask
NSURLSessionDataTask使用NSData来交换数据. NSURLSessionDataTask不支持后台会话模式。
a) 通过request对象或url创建
//通过一个给定的请求
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request;
//通过一个给定的URL.
- (NSURLSessionDataTask *)dataTaskWithURL:(NSURL *)url;
代理方法:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//data: response from the server.
}
b).通过request对象或url创建,同时指定任务完成后通过completionHandler指定回调的代码块:
- (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;
2).NSURLSessionDownloadTask
a).通过URL/request/resumeData创建
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request;
- (NSURLSessionDownloadTask *)downloadTaskWithURL:(NSURL *)url;
//下载任务支持断点续传,这种方式是通过之前已经下载的数据来创建下载任务。
- (NSURLSessionDownloadTask *)downloadTaskWithResumeData:(NSData *)resumeData;
代理方法:
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didWriteData:(int64_t)bytesWritten
totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes;
b).通过completionHandler指定任务完成后的回调代码块:
- (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;
3).NSURLSessionUploadTask
a).通过request创建,在上传时指定文件源或数据源。
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL;
- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData;
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request;
代理方法:
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didSendBodyData:(int64_t)bytesSent
totalBytesSent:(int64_t)totalBytesSent
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;
b).通过completionHandler指定任务完成后的回调代码块
- (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;
3.NSURLSession类
从上面我们已经知道创建一个NSURLSessionTask需要NSURLSession,获取NSURLSession类对象有几种方式:
//使用全局的Cache,Cookie和证书。
+ (NSURLSession *)sharedSession;
//创建对应配置的会话。
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration;
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id <NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue;
第三种方式指定了session的委托和委托所处的队列。当不再需要连接时,可以调用Session的invalidateAndCancel直接关闭,或者调用finishTasksAndInvalidate等待当前Task结束后关闭。这时Delegate会收到URLSession:didBecomeInvalidWithError:这个事件。Delegate收到这个事件之后会被解引用。
3.NSURLSessionConfiguration类
上面2、3种创建方式使用了NSURLSessionConfiguration,它用于配置会话的属性,创建方法如下:
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
//identifier参数指定了会话的ID,用于标记后台的session。
+ (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;
这个类有两个属性:
@property BOOL allowsCellularAccess;
@property (getter=isDiscretionary) BOOL discretionary NS_AVAILABLE(NA, 7_0);
allowsCellularAccess 属性指定是否允许使用蜂窝连接, discretionary属性为YES时表示当程序在后台运作时由系统自己选择最佳的网络连接配置,该属性可以节省通过蜂窝连接的带宽。在使用后台传输数据的时候,建议使用discretionary属性,而不是allowsCellularAccess属性,因为它会把WiFi和电源可用性考虑在内。
2.实例
创建一个NSURLSession任务需要遵循以下步骤:
a)创建一个Session Configuration
b)创建一个NSURLSession
c) 创建一个NSURLSession Task ( Data,Download or Upload)
d)代理方法
e)调用resume方法
1.数据请求
前面通过请求一个NSURLSessionDataTask进行数据请求演示:
- (IBAction)loadData:(UIButton *)sender {
// 创建Data Task,
NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com/users/9tsPFp/latest_articles"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
// 输出返回的状态码,请求成功的话为200
if (!error) {
[self showResponseCode:response];
}else{
NSLog(@"error is :%@",error.localizedDescription);
}
}];
// 使用resume方法启动任务
[dataTask resume];
}
- (void)showResponseCode:(NSURLResponse *)response {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSInteger responseStatusCode = [httpResponse statusCode];
NSLog(@"状态码--%ld", (long)responseStatusCode);
}
2.HTTP GET和POST
1).GET
我们使用豆瓣的API来演示这个GET请求
- (IBAction)GETRequest:(id)sender {
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: self delegateQueue: [NSOperationQueue mainQueue]];
NSURL * url = [NSURL URLWithString:@"https://api.douban.com/v2/book/1220562"];
NSURLSessionDataTask * dataTask = [delegateFreeSession dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"GET请求数据= %@",text);
}
}];
[dataTask resume];
}
数据请求结果如下:
2).POST
我们使用http://hayageek.com/examples/jquery/ajax-post/ajax-post.php 这个接口来演示POST请求,POST需要4个参数: name, loc, age, submit,如下:
- (IBAction)POSTRequest:(UIButton *)sender {
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSURL * url = [NSURL URLWithString:@"http://hayageek.com/examples/jquery/ajax-post/ajax-post.php"];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:url];
NSString * params =@"name=Ravi&loc=India&age=31&submit=true";
[urlRequest setHTTPMethod:@"POST"];
[urlRequest setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask * dataTask =[delegateFreeSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Response:%@ %@\n", response, error);
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(@"POST返回数据 = %@",text);
}
}];
[dataTask resume];
}
返回数据:
Response:<NSHTTPURLResponse: 0x7ff948778240> { URL: http://hayageek.com/examples/jquery/ajax-post/ajax-post.php } { status code: 200, headers {
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Type" = "text/html";
Date = "Wed, 18 Mar 2015 01:43:07 GMT";
Server = nginx;
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding, Accept-Encoding";
"X-Powered-By" = "EasyEngine 2.0.0";
} } (null)
2015-03-18 09:43:10.418 WebConnectionDemo[28129:2666176] POST返回数据 = Data from server: {"name":"Ravi","loc":"India","age":"31","submit":"true"}<br>
3.NSURLSessionUploadTask
Upload task 的创建需要使用一个 request,另外加上一个要上传的 NSData 对象或者是一个本地文件的路径对应的 NSURL,类似的代码如下:
NSURL *URL = [NSURL URLWithString:@"http://example.com/upload"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
NSData *data = ...;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request
fromData:data
completionHandler:
^(NSData *data, NSURLResponse *response, NSError *error) {
// ...
}];
[uploadTask resume];
4.NSURLSessionDownloadTask
a.普通下载
- (IBAction)download:(id)sender {
NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ec14b94082249759ee3c6ddbc6.jpg"];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];
NSURLSessionDownloadTask * downloadTask =[ defaultSession downloadTaskWithURL:url];
[downloadTask resume];
}
为了实现下载进度的显示,需要在委托中的以下方法中实现:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
float progress = totalBytesWritten*1.0/totalBytesExpectedToWrite;
dispatch_async(dispatch_get_main_queue(),^ {
[self.process setProgress:progress animated:YES];
});
NSLog(@"Progress =%f",progress);
NSLog(@"Received: %lld bytes (Downloaded: %lld bytes) Expected: %lld bytes.\n",
bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
}
Download task 是将数据一点点地写入本地的临时文件。我们需要把文件从一个临时地址移动到一个永久的地址保存起来::
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(@"Temporary File :%@\n", location);
NSError *err = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSURL *docsDirURL = [NSURL fileURLWithPath:[docsDir stringByAppendingPathComponent:@"out1.zip"]];
if ([fileManager moveItemAtURL:location
toURL:docsDirURL
error: &err])
{
NSLog(@"File is saved to =%@",docsDir);
}
else
{
NSLog(@"failed to move: %@",[err userInfo]);
}
}
如下所示:
b.可取消的下载
我们将做一些改动,使其支持取消下载,先创建一个全局NSURLSessionDownloadTask对象:
@property (strong, nonatomic) NSURLSessionDownloadTask *cancellableTask;
调用cancle方法即可取消:
- (IBAction)cancleDownload:(UIButton *)sender {
if (self.cancellableTask) {
[self.cancellableTask cancel];
self.cancellableTask = nil;
}
}
这样点击cancle按钮后下载任务会取消,重新点击下载会从最初的经度条开始下载。
c.断点续传下载
断点续传,我们需要一个NSData来暂存我们下载的数据:
@property (strong, nonatomic) NSData *partialData;
download方法中做以下改动,如果已经有缓存的数据,即使用downloadTaskWithResumeData进行下载:
- (IBAction)download:(UIButton *)sender {
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate:self delegateQueue: [NSOperationQueue mainQueue]];
if (self.partialData) {
self.cancellableTask = [defaultSession downloadTaskWithResumeData:self.partialData];
}
else{
NSURL * url = [NSURL URLWithString:@"http://e.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ec14b94082249759ee3c6ddbc6.jpg"];
self.cancellableTask =[ defaultSession downloadTaskWithURL:url];
}
[self.cancellableTask resume];
}
cancle方法也需要做改动,我们需要保存我们已下载的数据:
- (IBAction)cancleDownload:(UIButton *)sender {
if (self.cancellableTask) {
[self.cancellableTask cancelByProducingResumeData:^(NSData *resumeData) {
self.partialData = resumeData;
}];
self.cancellableTask = nil;
}
}
另外恢复下载时,NSURLSessionDownloadDelegate中的以下方法将被调用:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
NSLog(@"NSURLSessionDownloadDelegate: Resume download at %lld", fileOffset);
}
如下:
6.后台下载
首先创建一个全局对象:
@property (strong, nonatomic) NSURLSessionDownloadTask * backgroundDownloadTask;
配置下载任务:
- (IBAction)downoadBackground:(id)sender {
NSURL * url = [NSURL URLWithString:@"https://developer.apple.com/library/ios/documentation/General/Conceptual/CocoaEncyclopedia/CocoaEncyclopedia.pdf"];
NSURLSessionConfiguration * backgroundConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"backgroundtask1"];
NSURLSession *backgroundSeesion = [NSURLSession sessionWithConfiguration: backgroundConfig delegate:self delegateQueue: [NSOperationQueue mainQueue]];
self.backgroundDownloadTask =[ backgroundSeesion downloadTaskWithURL:url];
[self.backgroundDownloadTask resume];
}
在程序进入后台后,如果下载任务完成,AppDelegate中的对应方法将被调用:
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
NSLog(@"Save completionHandler");
self.completionHandler = completionHandler;
}
然后修改上面那个协议方法,如下:
-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
if (downloadTask == self.cancellableTask) {
NSLog(@"Temporary File :%@\n", location);
NSError *err = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSURL *docsDirURL = [NSURL fileURLWithPath:[docsDir stringByAppendingPathComponent:@"out1.zip"]];
if ([fileManager moveItemAtURL:location
toURL:docsDirURL
error: &err]){
NSLog(@"File is saved to =%@",docsDir);
}
else{
NSLog(@"failed to move: %@",[err userInfo]);
}
}else if(downloadTask == self.backgroundDownloadTask){
NSLog(@"Background URL session %@ finished events.\n", session);
AppDelegate * delegate =(AppDelegate *)[[UIApplication sharedApplication] delegate];
if(delegate.completionHandler)
{
void (^handler)() = delegate.completionHandler;
handler();
}
}
}
运行程序并退出当前程序后打印结果如下:
Received: 21127 bytes (Downloaded: 1439932 bytes) Expected: 1464162 bytes.
Progress =0.997860
Received: 21096 bytes (Downloaded: 1461028 bytes) Expected: 1464162 bytes.
Progress =1.000000
Received: 3134 bytes (Downloaded: 1464162 bytes) Expected: 1464162 bytes.
Background URL session <__NSURLBackgroundSession: 0x7fc5c357b560> finished events.
关于NSURLSessionConfiguration的配置策略你可以在这篇文章中找到。
1.AFNNetworking 2.0
你相信你一定知道AFNNetworking,不知道你还可以看看该作者的博文,所以我就不多说关于它的强大之处了,AFNetworking 提供了比NSURLSession更高层次的抽象,这篇文章主要总结AFNNetworking 2.0的几个常用方法。
1).GET和POST请求
GET:
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
POST:
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
在实际的开发中,我比较习惯把第三方的代码进行一层封装,如下我们把GET和POST请求封装起来:
//GET
+ (void)getWithPath:(NSString *)path params:(NSDictionary *)params success:(HttpSuccessBlock)success failure:(HttpFailureBlock)failure{
[self requestWithPath:path params:params success:success failure:failure method:@"GET"];
}
//POST
+ (void)postWithPath:(NSString *)path params:(NSDictionary *)params success:(HttpSuccessBlock)success failure:(HttpFailureBlock)failure{
[self requestWithPath:path params:params success:success failure:failure method:@"POST"];
}
typedef void (^HttpSuccessBlock)(id JSON);
typedef void (^HttpFailureBlock)(NSError *error);
...省略
+ (void)requestWithPath:(NSString *)path params:(NSDictionary *)params success:(HttpSuccessBlock)success failure:(HttpFailureBlock)failure method:(NSString *)method{
AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:kBaseURL]];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain",@"application/json", @"text/json", @"text/javascript", @"text/html", nil];
if ([method isEqual: @"GET"]) {
[manager GET:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
success(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"fail Get!!%@",error);
failure(error);
}];
}else if ([method isEqual:@"POST"]){
[manager POST:path parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"POST成功:%@",responseObject);
success(responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"fail POST!!%@",error);
failure(error);
}];
}
}
你有没有注意到上面我设置了如下代码:
manager.responseSerializer = [AFJSONResponseSerializer serializer];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain",@"application/json", @"text/json", @"text/javascript", @"text/html", nil];
因为AFNetworking的responseSerializer 属性只能默认只能处理特定的Content-Type,如果你想处理"text/html"等其它类型,你需要明确指定它的acceptableContentTypes。
2).多文件上传
如下,这是我写的第三方微博客户端中多图上传的实例代码:
[manager POST:@"2/statuses/upload.json"
parameters:@{@"access_token": accseeToken,
@"status" : encodeStatus,
@"visible" : @(_intVisible),
@"lat" : @(_latitude),
@"long" : @(_longtitude)}
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
NSMutableArray *images = [NSMutableArray arrayWithArray:weakSelf.images];
for (id asset in images) {
NSData *data = nil;
if ([asset isKindOfClass:[UIImage class]]) {
data = UIImageJPEGRepresentation(asset, 0.4);
}
if ([asset isKindOfClass:ALAsset.class]) {
UIImage *original = [UIImage imageWithCGImage: [[asset defaultRepresentation] fullScreenImage]];
data = UIImageJPEGRepresentation(original, 0.4);
}
[formData appendPartWithFileData:data name:@"pic" fileName:@"pic.jpg" mimeType:@"multipart/form-data"];
}
} success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"发送成功");
[self back];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
[self showFailHUD];
}];
3).多线程操作
如果你需要开启多个线程, 你需要使用AFHTTPRequestSerializer
,AFHTTPRequestOperation和NSOperationQueue
以下是AFNetworking的实例代码
NSMutableArray *mutableOperations = [NSMutableArray array];
for (NSURL *fileURL in filesToUpload) {
NSURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:fileURL name:@"images[]" error:nil];
}];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[mutableOperations addObject:operation];
}
NSArray *operations = [AFURLConnectionOperation batchOfRequestOperations:@[...] progressBlock:^(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations) {
NSLog(@"%lu of %lu complete", numberOfFinishedOperations, totalNumberOfOperations);
} completionBlock:^(NSArray *operations) {
NSLog(@"All operations in batch complete");
}];
[[NSOperationQueue mainQueue] addOperations:operations waitUntilFinished:NO];
3).网络状态检查
网络状态检查在早期都是通过苹果官方的Reachability类进行检查,但是这个类本身存在一些问题,并且官方后来没有再更新。我们可以直接使用AFNetworking框架检测。不管使用官方提供的类还是第三方框架,用法都是类似的,通常是发送一个URL然后去检测网络状态变化,网络改变后则调用相应的网络状态改变方法。如下:
-(void)alert:(NSString *)message{
UIAlertView *alertView=[[UIAlertView alloc]initWithTitle:@"System Info" message:message delegate:nil cancelButtonTitle:@"Cancel" otherButtonTitles: nil];
[alertView show];
}
-(void)checkNetworkStatus{
//创建一个用于测试的url
NSURL *url=[NSURL URLWithString:@"http://www.apple.com"];
AFHTTPRequestOperationManager *operationManager=[[AFHTTPRequestOperationManager alloc]initWithBaseURL:url];
//根据不同的网络状态改变去做相应处理
[operationManager.reachabilityManager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
[self alert:@"2G/3G/4G Connection."];
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
[self alert:@"WiFi Connection."];
break;
case AFNetworkReachabilityStatusNotReachable:
[self alert:@"Network not found."];
break;
default:
[self alert:@"Unknown."];
break;
}
}];
//开始监控
[operationManager.reachabilityManager startMonitoring];
}