People Lack Willpower,Rather Than Strength!
1.NSURLSession
- 本节主要涉及
- NSURLSession的两个get请求/一个post请求
- NSURLSessionTask抽象类,及其三个具象类:
- 1.NSURLSessionDataTask
- 2.NSURLSessionDownloadTask
- 3.NSURLSessionUploadTask
1.1 NSURLSession 基本使用
-
使用步骤
- 创建NSURLSession
- 创建Task
- 执行Task
-
默认情况下创建的请求都是GET类型
- GET1 : 使用request创建task
// dataTask NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // 1.创建session NSURLSession *session = [NSURLSession sharedSession]; // 2.使用session创建task NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { /* 参数说明: data:服务器返回给我们的数据; response:响应头 error:错误信息 */ // 通过该方法,我们可以对data进行简单处理 NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); // 2015-09-09 22:58:58.010 01-NSURLSession基本使用[3368:81761] {"success":"登录成功"} }]; // 3.执行task [task resume];
- GET2: 使用URL创建Task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=520it&pwd=520it&type=JSON"]; // 1.session创建 NSURLSession *session = [NSURLSession sharedSession]; // 2.使用session创建task // 如果通过传入url创建task,方法内部会自动创建一个request,略微省事; // 如果是发送get请求,或者不需要设置请求头信息,那么建议使用当前方法发送请求❤️ NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); /* 2015-09-09 22:58:29.822 01-NSURLSession基本使用[3348:81294] {"success":"登录成功"} */ }]; // 3.执行task [task resume];
-
POST方法
- 关键在于修改请求体中的HTTPMethod
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login"]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; request.HTTPMethod = @"POST"; request.HTTPBody = [@"username=520it&pwd=520it&type=JSON" dataUsingEncoding:NSUTF8StringEncoding]; // 1.创建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根据session,创建task NSURLSessionDataTask *task = [session dataTaskWithRequest:request]; NSLog(@"%@",task); //2015-09-09 22:57:28.105 01-NSURLSession基本使用[3322:80609] <__NSCFLocalDataTask: 0x7f86c170c850>{ taskIdentifier: 1 } { suspended } // 3.执行task [task resume];
1.2 NSURLSessionDownloadTask
NSURLSessionDownloadTask简单使用
-
注意:
- 默认情况下,使用NSURLSessionDownloadTask时,系统已经帮我们实现边下载边存储.防止内存暴增.
- 而需要我们做的只是将下载的资源从不安全的tmp文件夹挪到caches文件夹.
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; // NSURLSessionDownloadTask // 1.创建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根据session创建task NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { //查看是否下载成功.这里reponse正是类型是NSHTTPURLResponse❤️ NSHTTPURLResponse *resp = (NSHTTPURLResponse *)response; NSLog(@"%ld,%zd",resp.statusCode,resp.expectedContentLength); // 参数解释: // location 即是下载好的资源在沙盒中的地址 // NSURLSessionDownloadTask已经默认帮我们实现了,边下载边写入沙盒,以防内存暴增; // 查看默认下载地址 NSLog(@"%@",location); // 默认是在tmp文件夹下,不安全,我们需要做的只是把它移到我们需要的位置:caches // 使用NSFileManager NSFileManager *manager = [NSFileManager defaultManager]; // 将下载好的文件转移至caches // NSString *toPath = [[location lastPathComponent] cacheDir]; NSString *toPath = [response.suggestedFilename cacheDir]; [manager moveItemAtURL:location toURL:[NSURL fileURLWithPath:toPath] error:nil]; } // 3.执行task [task resume]; //========================================== // 通过url创建task [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>] // 通过上次下载中断中处创建新task-->断点下载 [session downloadTaskWithResumeData:<#(NSData *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>];
NSURLSessionDownloadTask监听下载进度
- downloadTask可以暂停/取消/恢复下载
- 这里,无论从suspended还是canle中resume,都是程序正常状态下的;
- 如果程序意外中断,downloadTask提供的方法还不足以完成,dataTask可以完成!❤️
- 注意: 使用代理方法时,不要使用block方法处理返回数据.否则代理方法失效.
- 监听下载进度
- (void)viewDidLoad
{
[super viewDidLoad];
// 保存下载路径
self.path = [@"minion_02.mp4" cacheDir];
}
/**开始下载 */
- (IBAction)download:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
// 1.创建session
/*
第一个参数:Session的配置信息
第二个参数: 代理
第三个参数: 决定了代理方法在哪个线程中执行
*/
self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根据session创建downloadTask
self.task = [self.session downloadTaskWithURL:url];
// 注意:这里如果使用回调方法,那么代理方法就不起作用了!!!❤️🈲
// [session downloadTaskWithURL:<#(NSURL *)#> completionHandler:<#^(NSURL *location, NSURLResponse *response, NSError *error)completionHandler#>]
// 3.执行Task
[self.task resume];
}
// ===========================================
// 接收到服务器反馈的数据是调用,开始写入数据
/*注意:该方法会调用一次或者多次
bytesWritten:当前写入数据量;
totalBytesWritten:总共写入数据量;
totalBytesExpectedToWrite:服务器返回给我们的文件大小
*/
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
self.progressView.progress = 1.0 * totalBytesWritten / totalBytesExpectedToWrite;
}
// 写完数据调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
NSLog(@"didFinishDownloadingToURL");
// 数据写入完成时,要把数据从tmp文件夹转移至caches
NSFileManager *manager = [NSFileManager defaultManager];
NSURL *toUrl = [NSURL fileURLWithPath:self.path];
[manager moveItemAtURL:location toURL:toUrl error:nil];
}
// 下载完成
// 如果调用该方法时,error有值,表示下载出现错误
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
// 恢复下载时调用
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
{
NSLog(@"didResumeAtOffset");
}
// ===================取消/恢复===================
/**暂停*/
- (IBAction)pause:(id)sender {
[self.task suspend];
}
/** 从暂停中恢复*/
- (IBAction)pause2goon:(id)sender {
[self.task resume];
}
/**取消 */
- (IBAction)cance:(id)sender {
// 注意这里如果使用这样的取消,那么就没办法恢复了!❤️
// [self.task cancel];
// 如果是调用cancelByProducingResumeData方法, 方法内部会回调一个block, 在block中会将resumeData传递给我们
// resumeData中就保存了当前下载任务的配置信息(下载到什么地方, 从什么地方恢复等等)❤️
[self.task cancelByProducingResumeData:^(NSData *resumeData) {
self.resumeData = resumeData;
}];
}
/** 从取消中恢复*/
- (IBAction)cance2goon:(id)sender {
// 从上次中断数据处新建下载任务
self.task = [self.session downloadTaskWithResumeData:self.resumeData];
[self.task resume];
}
1.3 NSURLSessionDataTask
NSURLSessionDataTask代理方法
- 注意:NSURLSessionDataTask的代理方法中,默认情况下是不接受服务器返回的数据的.如果想接受服务器返回的数据,必须手动告诉系统,我们需要接收数据⚠️
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 1.创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根据session创建Task
NSURLSessionDataTask *task = [session dataTaskWithRequest:request];
// 3.执行Task
[task resume];
}
// ===================代理方法====================
#pragma mark - NSURLSessionDataDelegate
// 服务器响应时调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
NSLog(@"didReceiveResponse");
// 参数解释:⚠️
// 系统默认是不会去调用didReceiveData和didCompleteWithError,必须手动告诉系统,我们需要接收数据
/* 可见:NSURLSessionResponseDisposition默认== 0 ,也就是说默认是不接收数据的❤️
typedef NS_ENUM(NSInteger, NSURLSessionResponseDisposition) {
NSURLSessionResponseCancel = 0, Cancel the load == -[task cancel]
NSURLSessionResponseAllow = 1, Allow the load to continue
NSURLSessionResponseBecomeDownload = 2, Turn this request into a download
*/
completionHandler(NSURLSessionResponseAllow);
}
// 收到服务器返回的数据时调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
NSLog(@"didReceiveData");
}
// 请求完毕时调用,如果error有值,代表请求失败
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
NSURLSessionDataTask断点下载
- 说明:
- 由于dataTask并不擅长下载任务,所以如果用其完成断点下载,那么还是得自己使用文件句柄,或者输出流完成! 同时在request中设置下载位置.
- 显然这并不是好的,但是使用擅长下载的downloadTask,貌似我们目前又很难从实现意外中断恢复继续下载;
- 注意: 这里是使用DataTask完成下载, 并不是专业的,无法从cancel重恢复.
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化操作
// 1.初始化路径
self.path = [@"" cacheDir];
// 2.初始化currentLength
self.currentLength = [self fileDataSize:self.path];
}
// ==============================================
#pragma mark - lazy
- (NSURLSession *)session
{
if (!_session) {
// 1.创建session
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
}
return _session;
}
- (NSURLSessionDataTask *)task
{
if (!_task) {
//如果想实现意外中断后的继续下载,NSURLSessionDataTask也需要通过设置请求头的Range来实现❤️❤️
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 设置请求头❤️⭐️◀️
NSString *range = [NSString stringWithFormat:@"bytes:%zd-",[self fileDataSize:self.path]];
[request setValue:range forHTTPHeaderField:@"Range"];
// 2.根据session创建Task
_task = [self.session dataTaskWithRequest:request];
}
return _task;
}
// 文件大小
- (NSUInteger)fileDataSize:(NSString *)path
{
NSFileManager *manager = [NSFileManager defaultManager];
NSDictionary *dict = [manager attributesOfItemAtPath:path error:nil];
return [dict[NSFileSize] integerValue];
}
// 输出流获得
- (NSOutputStream *)outputStream
{
if (!_outputStream) {
_outputStream = [NSOutputStream outputStreamToFileAtPath:self.path append:YES];
//输出流开启
[_outputStream open];
//NSLog(@"输出流");
}
return _outputStream;
}
/** 开始下载
*/
- (IBAction)download:(id)sender {
[self.task resume];
}
// ======================代理方法====================
#pragma mark - NSURLSessionDataDelegate
// 服务器响应时调用
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
{
completionHandler(NSURLSessionResponseAllow);
// 数据总大小
self.totalLength = response.expectedContentLength + [self fileDataSize:self.path];
// 感觉 self.currentLength = [self fileDataSize:self.path]😖可以试试
}
// 收到服务器返回的数据时调用,调用一次或者多次
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
// 输出流写入数据
[self.outputStream write:data.bytes maxLength:data.length];
// 计算进度
self.currentLength += data.length;
self.progressView.progress = 1.0 * self.currentLength / self.totalLength;
}
// 请求完毕时调用,如果error有值,代表请求失败,由于没有didFinish方法,所以一旦完成下载,就会掉该方法,和downloadTask不同!❤️
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
// 关闭输出流
[self.outputStream close];
}
1.4.NSURLSessionUploadTask
1.4.1.文件上传
-
说明:
- 和NSURLConnection中的文件上传区别在于:
- 设置请求体时,不可以request.HTTPBody = bodyData ; 需要将bodyData设置到创建的task中.
// 设置请求头 request.HTTPMethod = @"POST"; [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", XMGBoundary] forHTTPHeaderField:@"Content-Type"]; // 设置请求体 NSMutableData *body = [NSMutableData data]; // 文件参数 // 分割线 [body appendData:XMGEncode(@"--")]; [body appendData:XMGEncode(XMGBoundary)]; [body appendData:XMGNewLine]; ..... // 注意这里通过设置请求体 = data完成文件上传,官方说这样做会被忽略 // 就是说, 如果利用NSURLSessionUploadTask上传文件, 那么请求体必须写在fromData参数中, 不能设置在request中. 否则设置在request中会被忽略 /* The body stream and body data in this request object are ignored. */ // request.HTTPBody = data; ❌ // 1.创建session NSURLSession *session = [NSURLSession sharedSession]; // 2.根据session创建Task //注意这里的data是文件参数和非文件参数的拼接二进制❤️ NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData: body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSLog(@"%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]); }]; // 3.执行Task [task resume]; // 注意:不要使用这个方法. fromFile方法是用于PUT请求上传文件的 // 而我们的服务器只支持POST请求上传文件 [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {}];❌
- 和NSURLConnection中的文件上传区别在于:
1.4.2.文件上传的监听
- 关键: 设置代理及实现对应代理方法即可
- 核心代码:
........
// 1.创建session
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
// 2.根据session创建Task
// 该方法中有回调函数,会影响代理方法调用🈲
// NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { }];
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:request fromData:body];
// 3.执行Task
[task resume];
// =================代理方法====================
#pragma mark - NSURLSessionTaskDelegate
// 上传过程中调用
/*
bytesSent:当前这一次上传的数据大小;
totalBytesSent:总共上传数据大小
totalBytesExpectedToSend:需要上传的文件大小
*/
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
NSLog(@"didSendBodyData");
NSLog(@"%f",1.0 * totalBytesSent/totalBytesExpectedToSend);
}
// 请求完毕时调用
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
NSLog(@"didCompleteWithError");
}
2.AFN
2.1 NSURLConnection的封装
- 关键: 拿到AFHTTPRequestOperationManager 对象
- get 方法
// 1.创建manager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// 2.使用manager发送get任务
/*
NSString:需要请求的url地址字符串
parameters: 请求是需要传递的参数,需要以字典形式
success:请求成功时回调的函数
failure:请求失败时的回调函数
*/
// 注意AFNetworking的get方法发送请求时,参数和资源地址是分开写的!❤️
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager GET:path parameters:para success:^(AFHTTPRequestOperation *operation, id responseObject) {
/*
responseObject:
这里默认服务器返回给我们的数据是JSON数据,然后会自动把数据转换为OC对象;
如果真实返回类型不是JSON,那么默认情况下不会回调success block,直接回调failure block
*/
NSLog(@"%@",responseObject);
/*服务器返回数据是JSON数据时,打印如下:
2015-09-09 14:58:41.587 08-ANF基本使用[3605:115247] {
success = "\U767b\U5f55\U6210\U529f";
}
*/
} failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(@"error");
/*当服务器返回数据类型不是JSON时,直接回调failure函数
2015-09-09 15:00:14.309 08-ANF基本使用[3685:116977] error
*/
}];
- post 方法
// 1.创建requestOperationManager
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
// 2.使用manager发送post请求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:para success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(@"error");
}];
2.2 NSURLSession的封装
- 关键: 拿到AFHTTPSessionManager 对象
- get 方法
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager发送get请求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager GET:path parameters:para success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
- post 方法
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager发送post请求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *para = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:para success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
2.3 AFN下载
-
download
- 注意 : 该方法需要resume
// 1.创建manager AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; // 2.使用manager创建下载任务 NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]]; NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) { // 请求完成的回调 // targetPath:文件默认下载后保存的路径 // response:响应头 // NSURL:block返回值,告诉AFN框架,是否需要将下载的文件转移到其他地方 NSString *path = [response.suggestedFilename cacheDir]; return [NSURL fileURLWithPath:path]; } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) { // 下载完成的回调 // filePath:移动之后的文件路径 NSLog(@"filePath=%@",filePath); /* 2015-09-09 15:44:08.476 08-ANF基本使用[4101:136108] filePath=file:///Users/PlwNs/Library/Developer/CoreSimulator/Devices/80A80097-63B4-4AB9-8B6B-1A30CCF465BE/data/Containers/Data/Application/0D45FB84-ED1B-4D5B-8401-B9FF2A9AB386/Library/Caches/minion_02.png */ }]; // 3.恢复下载 [task resume];
-
监听下载进度
-
关键: 需要使用KVO 进行属性监听.
- 主代码
-(void)monitorDownloadProgress
{
// 1.创建sessionManager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.使用sessionManager创建task
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_02.mp4"]];NSProgress *progress = nil;
self.progress = progress;
//说明,这里任务被加入线程循环中,然后根据progress的地址,不断根据下载进度不断更新progress,所以我们才可以监听进度❤️
NSURLSessionDownloadTask *task = [manager downloadTaskWithRequest:request progress:&progress destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
NSString *path = [response.suggestedFilename cacheDir];
return [NSURL fileURLWithPath:path];
} completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError error) {
NSLog(@"filePath=%@",filePath);
}];
// 上述方法只会被调用一次,无法监控progress,只能使用属性和代理方法⚠️;
// 不过可惜,这里无法设置代理,只能通过KVO,通过KVO,那么这里我们就无需定义属性了!!!❤️
// 那么如何通过监控progress,拿到下载进度那?经查勘了解到,progress有两个属性
/NSProgress只是一个对象!如何跟踪进度!-> KVO 对属性变化的监听!
@property int64_t totalUnitCount: 需要下载的文件的总大小
@property int64_t completedUnitCount:已经下载的文件大小
*/
// 让self监控progress的属性变化
[progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];// 3.task resume
[task resume];
/*结果如下
2015-09-09 16:12:26.451 08-ANF基本使用[4430:150386] 0.00027
2015-09-09 16:12:26.509 08-ANF基本使用[4430:150388] 0.00043
2015-09-09 16:12:26.585 08-ANF基本使用[4430:150386] 0.00088
...................
*/
}``` - KVO ```objc -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // 根据开发经验,由于一个project中可能存在诸多被observed对象,所以在coding时,一定记得判断object类型❤️❤️❤️❤️❤️ // 与跳转相似 if ([object isKindOfClass:[NSProgress class]]) { NSProgress *p = (NSProgress *)object; // 下载进度 NSLog(@"%f",1.0 * p.completedUnitCount / p.totalUnitCount); // 获得准确进度 /** 准确的获得进度 localizedDescription 10% localizedAdditionalDescription completed 32,768 of 318,829 fractionCompleted 0.102776(completedUnitCount/totalUnitCount) */ NSLog(@"%@, %@, %f", p.localizedDescription, p.localizedAdditionalDescription, p.fractionCompleted); } } ``` - 勿忘移除监听❤️ ```objc -(void)dealloc { [self.progress removeObserver:self forKeyPath:@"completedUnitCount"]; } ```
-
2.4 upload
- 注意: AFNetworking 框架上传数据时,不是使用uploadTask ,如果这个,还得拼接上传请求体格式💔⚠️
- 我们使用AFN中的POST: constructingBodyWith: 方法
- (void)upload
{
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 2.利用manager post文件
NSString *path = @"http://120.25.226.186:32812/upload";
NSDictionary *para = @{
@"username":@"pj"
};
/*
参数说明:
参数一:上传服务器地址;
参数二:非文件参数;
formData:用来存储需要用来上传的文件二进制数据
*/
[manager POST:path parameters:para constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// 在这个block中上传文件数据
// formData就是专门用于保存需要上传文件的二进制数据的
// formData如何存储数据?三个方法:
// 1.appendPartWithFileData:
/*参数说明
NSData: 需要上传的文件二进制数据
name: 上传服务器字段(服务器对应的参数名称)
fileName:服务器上保持该文件的名称
mimeType:文件content-type(MIMETYPE)
*/
NSData *data = [NSData dataWithContentsOfFile:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileData:data name:@"file" fileName:@"table.png" mimeType:@"image/png"];
/*
2015-09-09 17:04:41.693 08-ANF基本使用[4692:168259] 成功回调{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//--------------------------------------------------
// 2.appendPartWithFileURL:
NSURL *url = [NSURL fileURLWithPath:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileURL:url name:@"file" error:nil];
/*
2015-09-09 17:08:15.797 08-ANF基本使用[4770:170215] 成功回调{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//---------------------------------------------------
// 3.appendPartWithFileURL:
NSURL *url = [NSURL fileURLWithPath:@"/Users/PlwNs/Desktop/座次表.png"];
[formData appendPartWithFileURL: url name:@"file" fileName:@"abc.png" mimeType:@"image/png" error:nil];
/*
2015-09-09 17:09:50.544 08-ANF基本使用[4806:171112] 成功回调{
success = "\U4e0a\U4f20\U6210\U529f";
}
*/
//---------------------------------------------------
// 4.注意该方法不是用来上传数据的🈲
// [formData appendPartWithFormData:<#(NSData *)#> name:<#(NSString *)#>]
// } success:^(NSURLSessionDataTask *task, id responseObject) {
//} failure:^(NSURLSessionDataTask *task, NSError *error) {
//}];
// 这个方法也不行,还得拼接二进制data,太麻烦了!!❤️
// NSURLSessionUploadTask *task = [manager uploadTaskWithRequest:request fromData:<#(NSData *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
// 这个是PUT请求,post请求不能使用🈲
// [manager uploadTaskWithRequest:<#(NSURLRequest *)#> fromFile:<#(NSURL *)#> progress:<#(NSProgress *__autoreleasing *)#> completionHandler:<#^(NSURLResponse *response, id responseObject, NSError *error)completionHandler#>];
}
2.5 序列化
- JSON数据
- (void)serializerJSON
{
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 如果现实不出来,加上下面两句话❤️ 一般情况不会解析不出来,因为AF框架默认就是解析JSON数据.
manager.responseSerializer = [AFJSONResponseSerializer serializer];
[AFJSONResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/json"];
// 2.根据manager执行post login请求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *paraDict = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"JSON"
};
[manager POST:path parameters:paraDict success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
/*很显然,被AFN框架自动转换成字典对象了
2015-09-09 18:02:24.647 08-ANF基本使用[5863:209466] {
success = "\U767b\U5f55\U6210\U529f";
}
*/
// 如果上面字典中type 不是 JSON , 那么直接进入failure!除非提前说明.
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
- XML数据
- 如果服务器返回的数据不是JSON,我们如何提前通知AFN框架?
- 如果提前告知AFN框架服务器返回数据是XML类型,那么框架就会将返回一个解析器对象(也作出了处理)❤️
- (void)serializerXML
{
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 告诉AFN框架,服务器返回数据类型
// 1.1 AFN将服务器返回数据看做是XML类型,不做处理
manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
// 如果还出错,加下面这句话❤️
[AFXMLParserResponseSerializer serializer].acceptableContentTypes = [NSSet setWithObject:@"text/xml"];;
// 2.根据manager执行post login请求
NSString *path = @"http://120.25.226.186:32812/login";
NSDictionary *paraDict = @{
@"username":@"520it",
@"pwd":@"520it",
@"type":@"XML"
};
[manager POST:path parameters:paraDict success:^(NSURLSessionDataTask *task, id responseObject) {
//只要设置AFN的responseSerializer为XML, 那么返回的responseObject就是NSXMLParser解析器,而不是数据对象了❤️
NSLog(@"%@",responseObject);
/*
08-ANF基本使用[6150:229599] <NSXMLParser: 0x7fa95ccc9920>
*/
// 这里可以再解析
// NSXMLParser *parser = (NSXMLParser *)responseObject;
// parser.delegate = self;
// [parser parse];
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
- 二进制数据
- 如果提前告知AFN框架服务器返回数据是二进制类型,也就是说不做任何处理❤️
- (void)serializer
{
// 1.创建manager
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
// 告诉AFN框架,服务器返回数据类型
// 1.2 AFN将服务器返回数据看做是二进制类型,也就是说不做任何处理❤️
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
// 2.根据manager执行post login请求
// 测试下载
NSString *path = @"http://120.25.226.186:32812/resources/images/minion_02.png";
// 这种写法POST/GET都适用❤️
// POST方法为什么不行!!!😖😖😖
// -------->模拟器问题,reset下就好了他妈的干干干.....
[manager POST:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(@"%@",responseObject);
/* 结果:
2015-09-09 19:44:02.707 08-ANF基本使用[6642:243829] <3c21444f 43545950 45206874 6d6c2050 55424c49 4320222d 2f2f5733 432f2f44 54442048 544d4c20 342e3031 20547261 6e736974 696f6e61 6c2f2f45 4e222022 68747470 3a2f2f77 77772e77 332e6f72 672f5452 ...........
*/
} failure:^(NSURLSessionDataTask *task, NSError *error) {
NSLog(@"error");
}];
}
2.6 AFN 解耦
- KEY :
- 自定义单例继承Manager
- 优点: 替换框架只需要求改单例类即可
- NSURLConnection 封装
// PJNetworkingTool1.h 文件==================
#import "AFHTTPRequestOperationManager.h"
@interface PJNetworkTool1 : AFHTTPRequestOperationManager
- (instancetype)shareManager;
@end
// PJNetworkingTool1.m文件==================
#import "PJNetworkTool1.h"
@implementation PJNetworkTool1
- (instancetype)shareManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 一般来说,为了重构,我们不这样写!❤️
// instance = [self shareManager];
NSString *urlStr = @"http://120.25.226.186:32812/";
instance = [[PJNetworkTool1 alloc] initWithBaseURL:[NSURL URLWithString:urlStr]];
});
return instance;
}
@end
- NSURLSession 封装
// .h文件
#import "AFHTTPSessionManager.h"
@interface PJNetworkTool2 : AFHTTPSessionManager
- (instancetype)shareManager;
@end
// .m 文件
#import "PJNetworkTool2.h"
@implementation PJNetworkTool2
- (instancetype)shareManager
{
static id instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *urlStr = @"http://120.25.226.186:32812/";
instance = [[PJNetworkTool2 alloc] initWithBaseURL:[NSURL URLWithString:urlStr] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
});
return instance;
}
@end
2.7 AFN问题
- Xcode 7之后,使用手动导入AFNetworking,会有问题:
- 不使用Cocoapods时,post下载总是不成功的!!
3.网络监测
3.1 苹果官方做法
//苹果自家的网络监控
#import "Reachability.h"
- (void)viewDidLoad {
[super viewDidLoad];
// 1.创建reachability对象(蜂窝网/局域网都行)
self.network = [Reachability reachabilityForLocalWiFi];
// 2.让self通过通知中心监听reachability状态变化
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getNetworkStatus) name:kReachabilityChangedNotification object:nil];
// 3.reachability开始发通知
[self.network startNotifier];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)getNetworkStatus
{
// 判断蜂窝网是否可得
if ([Reachability reachabilityForInternetConnection].currentReachabilityStatus != NotReachable) {
NSLog(@"当前为蜂窝网");
}else if([Reachability reachabilityForLocalWiFi].currentReachabilityStatus != NotReachable){ // 判断局域网是否可得
NSLog(@"当前为局域网");
}else{
NSLog(@"没有网络");
}
}
3.2 AFNetworking
- (void)AFMonitorNetwork
{
// 首先看看AFN框架如何做到监控网络状态
// 1.创建网络监听管理者
// 单例!
AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager];
//AFNetworkReachabilityManager *manager1 = [AFNetworkReachabilityManager sharedManager];
//NSLog(@"%@\n%@",manager,manager1);
/*
<AFNetworkReachabilityManager: 0x7ff5618ab3a0>
<AFNetworkReachabilityManager: 0x7ff5618ab3a0>
*/
// 2.设置网络变化时的回调block
/*
AFNetworkReachabilityStatusUnknown = 不能识别,
AFNetworkReachabilityStatusNotReachable = 没有网络,
AFNetworkReachabilityStatusReachableViaWWAN = 蜂窝网,
AFNetworkReachabilityStatusReachableViaWiFi = 局域网,
*/
[manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
switch (status) {
case AFNetworkReachabilityStatusReachableViaWWAN:
NSLog(@"蜂窝网");
break;
case AFNetworkReachabilityStatusReachableViaWiFi:
NSLog(@"局域网");
break;
case AFNetworkReachabilityStatusNotReachable:
NSLog(@"没有网");
break;
default:
NSLog(@"不能识别");
break;
}
}];
// 3.开始监听:这样就持续不断的监听了.....o(╯□╰)o
[manager startMonitoring];
/*2015-09-09 22:16:44.966 10-网络监测[2658:55183] 局域网*/
}