iOS - NSURLConnection&&NSURLSession

作者:Mitchell 

一、NSURLConnection

  • iOS7之后不建议使用
  • GET请求
    • 发送同步请求
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"httpAddress"];
    // 2.根据URL创建NSURLRequest对象
    // 默认情况下NSURLRequest会自动给我们设置好请求头
    // request默认情况下就是GET请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    // 3.利用NSURLConnection对象发送请求
    /*
     第一个参数: 需要请求的对象
     第二个参数: 服务返回给我们的响应头信息
     第三个参数: 错误信息
     返回值: 服务器返回给我们的响应体
     */
    //    NSURLResponse *response = nil;
    NSHTTPURLResponse *response = nil; // 真实类型
    // 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
  • 发送异步请求
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"httpAddress"];
    // 2.根据URL创建NSURLRequest对象
    // 默认情况下NSURLRequest会自动给我们设置好请求头
    // request默认情况下就是GET请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // 3.利用NSURLConnection对象发送请求
    /*
     第一个参数: 需要请求的对象
     第二个参数: 回调block的队列, 决定了block在哪个线程中执行
     第三个参数: 回调block
     */
    // 注意点: 如果是调用NSURLConnection的同步方法, 会阻塞当前线程
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        /*
         response: 响应头
         data : 响应体
         connectionError : 错误信息
         */
        //        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
  • POST请求:
    // 1.创建一个URL
    NSURL *url = [NSURL URLWithString:@"http://admin/login"];
    // 2.根据URL创建NSURLRequest对象
//    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    /*
     NSMutableURLRequest中保存了请求的地址/请求头/请求体
     */
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    // 2.1设置请求方式
    // 注意: POST一定要大写
    request.HTTPMethod = @"POST";
    // 2.2设置请求体
    // 注意: 如果是给POST请求传递参数: 那么不需要写?号
    request.HTTPBody = [@"username=Mitchell&pwd=123456&type=JSON" dataUsingEncoding:NSUTF8StringEncoding];
    
    // 3.利用NSURLConnection对象发送请求
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    }];
  • NSURLConnection 与 NSRunLoop 的关联使用
    • 主要是区分 NSURLConnection 在主线程和子线程发送网络请求的区别
    • 主线程
      • 1、 直接发送网络请求,发送是异步的,但是代理方法是在主线程中执行的:
    NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //这里分两种方式发送请求
    //2.1 直接发送网络请求是异步的,但是回调方法是在主线程中执行的
    //[[NSURLConnection alloc]initWithRequest:request delegate:self];
    * 2、如果按照如下设置,那么回调的代理方法也会运行在子线程中:
NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //2.2 设置回调方法也在子线程中运行
    NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
    [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
    [conn start];
- 子线程
    * `因为 NSURLConnection 是局部变量`,当我们创建的时候其实是会默认`添加到当前的 RunLoop 中`,如果是在主线程添加,主线程的 RunLoop 是默认有的,无须我们创建,`然而如果再子线程中,是默认没有 RunLoop 和输入源的,所以需要给子线程手动添加 RunLoop` 。
    * 为什么使用 `start` 方法就可以呢?

因为 start 方法如果没有 RunLoop ,会默认添加一个 RunLoop 到当前的线程中来。

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue, ^{
        NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
        NSURLRequest*request = [NSURLRequest requestWithURL:url];
        
        //2.1 这么设置还是可以的
//NSURLConnection*conn = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
//[conn setDelegateQueue:[[NSOperationQueue alloc] init]];
//[conn start];
        
        //2.2 但是这样设置就不行了
//[NSURLConnection connectionWithRequest:request delegate:self];
        /*问题一:为什么?
         NSURLConnection 是临时变量,当运行的时候被默认添加到当前线程的 RunLoop 中,由于主线程的RunLoop是默认存在的,所以可以运行,这里不行能的原因就是当前线程并没有 RunLoop,如果想让其运行,比需要创建RunLoop。
         问题二:为什么2.1就可以?
         因为start方法:如果没有一个runloop,它会自动给当前线程添加runLoop,然后将connection加到runLoop中。
         If you don’t schedule the connection in a run loop or an operation queue before calling this method, the connection is scheduled in the current run loop in the default mode.
         */
        //2.2解决方式
       NSRunLoop*loop =  [NSRunLoop currentRunLoop];
        [NSURLConnection connectionWithRequest:request delegate:self];
        [loop run];
    });

二、NSURLSession

  • 推荐使用
  • 简单使用
    • Get
    //注意:如果需要改请求头 需要使用这种方法
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        /*
         data:服务器返回高i的数据
         response:响应头
         error:错误信息
         */
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
    * 
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    // 如果是通过url的方法创建Task,方法内部会自动根据URL创建一个request
    // 如果是发送Get请求,或者不需要设置请求头信息,那么建议使用当前方法发送请求
    NSURLSessionDataTask*task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
- POST
    NSURL*url = [NSURL URLWithString:@"httpAddress"];
    NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"POST";
    request.HTTPBody =@"需要发送的信息";
    //1、创建NSURLSession
    NSURLSession*session = [NSURLSession sharedSession];
    //2、利用NSURLSession创建Tash
    NSURLSessionDataTask*task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSLog(@"%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
    }];
    //3、执行Task
    [task resume];
  • 断点下载
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController () 
@property (weak, nonatomic) IBOutlet UIProgressView *pro;
@property (weak, nonatomic) IBOutlet UIButton *startBtn;
@property (weak, nonatomic) IBOutlet UIButton *cancelBtn;
@property (nonatomic,strong) NSURLSession             * session;
@property(nonatomic,assign)CGFloat totalLength;
@property(nonatomic,assign)CGFloat currentLength;
@property(nonatomic,strong)NSURLSessionDataTask*task;
@property(nonatomic,strong)NSOutputStream * stream;
@property(nonatomic,strong)NSString * path;
@end
@implementation ViewController
  - (void)viewDidLoad {
    [super viewDidLoad];
    //初始化操作
    //1、初始化文件路径
    self.path = [@"abc.mp4" cacheDir];
    //2、初始化当前下载进度
    self.currentLength = [self getFileSizeWithPath:self.path];
 }
 #pragma mark ------------------ delegate ------------------
 //注意:NSURLSessionDataTask的代理方法中,默认情况下是不接受服务器返回的数据的,所以didCompleteWithError、didReceiveData默认不会被调用
 //如果想接受服务器返回的数据,必须手动告诉系统,我们需要接受数据
  - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
    NSLog(@"didReceiveResponse");
    self.totalLength = response.expectedContentLength;
    //告诉服务器接受,才能调用didCompleteWithError、didReceiveData两个方法
    NSString*path = [@"abc.mp4" cacheDir];
    NSLog(@"%@",path);
    _stream = [NSOutputStream outputStreamToFileAtPath:path append:YES];
    [_stream open];
    completionHandler(NSURLSessionResponseAllow);
}
  -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
    self.currentLength +=data.length;
    self.pro.progress = 1.0*_currentLength/_totalLength;
    [_stream write:data.bytes maxLength:data.length];
    NSLog(@"didReceiveData");
}
  -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    NSLog(@"didCompleteWithError");
    [_stream close];
    _stream = nil;
}
  - (NSURLSession *)session{
    if (!_session) {
        //1、创建NSURLSession
        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}
  - (NSURLSessionDataTask *)task{
    if (!_task) {
        //图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
        //MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
        NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
        NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];
        NSString*range = [NSString stringWithFormat:@"bytes:%zd-",[self getFileSizeWithPath:self.path]];
        [request setValue:range forHTTPHeaderField:@"Range"];
        //2、利用NSURLSession创建Tash
        _task = [self.session dataTaskWithURL:url];
    }
    return _task;
}
  - (NSUInteger)getFileSizeWithPath:(NSString*)path{
    NSUInteger currentSize = [[[NSFileManager defaultManager]attributesOfItemAtPath:path error:nil][NSFileSize] integerValue];
    return currentSize;
}
 #pragma mark ------------------ 开始 ------------------
  - (IBAction)start:(id)sender {
    //3、执行Task
    [self.task resume];

}
 #pragma mark ------------------ 暂停 ------------------
  - (IBAction)pause:(id)sender {
    NSLog(@"暂停");
    [self.task suspend];
}
 #pragma mark ------------------ 继续 ------------------
  - (IBAction)resume:(id)sender {
    NSLog(@"继续");
    [self.task resume];
}
 #pragma mark ------------------ 取消 ------------------
  - (IBAction)cancel:(id)sender {
}
@end
  • 下载进度
#import "ViewController.h"
#import "NSString+Mitchell.h"
@interface ViewController ()
@property (weak, nonatomic ) IBOutlet UIProgressView           *pro;
@property (weak, nonatomic ) IBOutlet UIButton                 *startBtn;
@property (weak, nonatomic ) IBOutlet UIButton                 *cancelBtn;
@property (nonatomic,strong) NSURLSessionDownloadTask * task;
@property (nonatomic,strong) NSData                   * resumeData;
@property (nonatomic,strong) NSURLSession             * session;
@end
@implementation ViewController
   - (void)viewDidLoad {
    [super viewDidLoad];
}
#pragma mark ------------------ didFinishDownloadingToURL ------------------
//下载完成时调用
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location{
    NSString*toPath = [@"abc.mp4" cacheDir];
    NSURL*toUrl = [NSURL fileURLWithPath:toPath];
    NSFileManager *manager = [NSFileManager defaultManager];
    [manager moveItemAtURL:location toURL:toUrl error:nil];
    NSLog(@"%@",toUrl);
    NSLog(@"didFinishDownloadingToURL");
    _startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ didWriteData ------------------
//接收服务器返回的数据时调用
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    //bytesWritten:每次接收了多少
    //totalBytesWritten:本地写入了多少
    //totalBytesExpectedToWrite:一共多少
NSLog(@"%zd,%zd,%zd",bytesWritten,totalBytesWritten,totalBytesExpectedToWrite);
    self.pro.progress = 1.0* totalBytesWritten/totalBytesExpectedToWrite;
}
#pragma mark ------------------ didResumeAtOffset ------------------
//恢复下载时候调用(使用resumeData恢复下载的时候才能调用)
 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes{
    NSLog(@"didResumeAtOffset");
}
//下载完成时调用
//如果下载时候error有值,代表着下载出错
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    NSLog(@"didCompleteWithError");
    _startBtn.userInteractionEnabled = YES;
}
#pragma mark ------------------ 开始 ------------------
 - (IBAction)start:(id)sender {
    //图片:http://img1.imgtn.bdimg.com/it/u=298400068,822827541&fm=21&gp=0.jpg%2F2008-09-08%2F200898163242920_2.jpg&bdtype=0&fr=ala&ala=1&alatpl=others&pos=1
    //MP4:http://mvvideo1.meitudata.com/55d99e5939342913.mp4
    NSURL*url = [NSURL URLWithString:@"http://mvvideo1.meitudata.com/55d99e5939342913.mp4"];
    NSURLRequest*request = [NSURLRequest requestWithURL:url];
    //1、创建NSURLSession
    /*
     第一个参数:Session的配置信息
     第二个参数:代理
     第三个参数:规定了代理方法在哪个线程中执行
     */
    _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //2、利用NSURLSession创建Task
    _task = [self.session downloadTaskWithRequest:request];
    //3、执行Task
    [_task resume];
    _startBtn.userInteractionEnabled = NO;
}
#pragma mark ------------------ 暂停 ------------------
 - (IBAction)pause:(id)sender {
    NSLog(@"暂停");
    [_task suspend];
}
#pragma mark ------------------ 继续 ------------------
  - (IBAction)resume:(id)sender {
    NSLog(@"继续");
//    [_task resume];
    //恢复信息恢复下载,用获取到resumeData继续下载
    self.task = [_session downloadTaskWithResumeData:_resumeData];
    [self.task resume];
}
#pragma mark ------------------ 取消 ------------------
 - (IBAction)cancel:(id)sender {
    //取消
    //注意点:一旦取消,就不能恢复了
//    [_task cancel];
    //如果是调用了cancelByProducingResumeData方法,方法内部会回调一个block,在block中会将resumeData传递给我们
    //resumeData中就保存了当前下载任务的配置信息(下载到什么地方,从什么地方恢复等等)
    [self.task cancelByProducingResumeData:^(NSData *resumeData) {
        self.resumeData = resumeData;
    }];
}
@end
  • 上传
#import "ViewController.h"
// 请求头的
#define MitchellHeaderBoundary @"----xiaomage"
// 请求体的
#define MitchellBoundary [@"------xiaomage" dataUsingEncoding:NSUTF8StringEncoding]
// 结束符
#define MitchellEndBoundary [@"------xiaomage--" dataUsingEncoding:NSUTF8StringEncoding]
// 将字符串转换为二进制
#define MitchellEncode(str) [str dataUsingEncoding:NSUTF8StringEncoding]
// 换行
#define MitchellNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
@interface ViewController ()
@end
@implementation ViewController
 - (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    NSURL*url = [NSURL URLWithString:@""];
    NSMutableURLRequest*request = [NSMutableURLRequest requestWithURL:url];    
    //1、创建Session
    NSURLSession*session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    //2、根据Seesion创建Task
    /*
    //注意:fromFile方法是用于PUT请求上传文件的
    //而我们的服务器只支持POST请求上传文件
    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
    NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromFile:fileUrl completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    }];
     */
    /*请求体
     第一个参数:需要请求的地址/请求头/
     第二个参数:需要上传拼接之后的二进制数据
     */
    request.HTTPMethod = @"POST";
//    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
//    NSData*data =[NSData dataWithContentsOfURL:fileUrl];
    // 2.2设置请求体
    NSMutableData *data = [NSMutableData data];
    // 2.2.1设置文件参数
    [data appendData:MitchellBoundary];
    [data appendData:MitchellNewLine];
    // name : 对应服务端接收的字段类型(服务端参数的名称)
    // filename: 告诉服务端当前的文件的文件名称(也就是告诉服务器用什么名称保存当前上传的文件)
    [data appendData:[@"Content-Disposition: form-data; name=\"file\"; filename=\"videos.plist\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:[@"Content-Type: application/octet-stream" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    //上传文件的数据
    NSURL*fileUrl = [NSURL fileURLWithPath:@""];
    NSData*imgData = [NSData dataWithContentsOfURL:fileUrl];    
    [data appendData:imgData];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    // 2.2.2设置非文件参数
    [data appendData:MitchellBoundary];
    [data appendData:MitchellNewLine];
    // name : 对应服务端接收的字段类型(服务端参数的名称)
    [data appendData:[@"Content-Disposition: form-data; name=\"username\"" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    [data appendData:MitchellNewLine];
    [data appendData:[@"Mitchell" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:MitchellNewLine];
    // 2.2.3设置结束符号
    [data appendData:MitchellEndBoundary];
     //注意点:如果利用NSURLSessionUploadTask上传文件,那么请求体必须卸载fromData参数中,不能设置在request中
    //如果设置在request中会被忽略
    //
    request.HTTPBody = data;
    NSURLSessionUploadTask*task = [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { 
    }];
    //3、执行Task
    [task resume];   
}
#pragma mark ------------------ Delegate ------------------
//上传过程中调用
//bytesSent:当前这一次上传的数据大小
//totalBytesSent:总共已经上传的数据大小
//totalBytesExpectedToSend:需要上传文件的大小
 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
NSLog(@"%f",1.0*totalBytesSent/totalBytesExpectedToSend);
}
//请求完毕时调用
 -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
    //用block方式 创建是不会调用这个方法
}
@end

你可能感兴趣的:(iOS - NSURLConnection&&NSURLSession)