iOS - NSURLConnection&&N

作者: Mitchell | 来源:发表于2015-09-09 22:43 被阅读763次
    作者: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 () <NSURLSessionDataDelegate>
    @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 ()<NSURLSessionDownloadDelegate>
    @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 ()<NSURLSessionTaskDelegate>
    @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
    

    相关文章

      网友评论

      • 95c9800fdf47:有没有demo,发一个demo看看过程!多谢!

      本文标题:iOS - NSURLConnection&&N

      本文链接:https://www.haomeiwen.com/subject/ztrpcttx.html