NSURLSession

2017-02-21  本文已影响26人  952625a28d0d

URLConnection

URL -> NSURLRequest -> URLConnect

NSURLSession

DataTask
DownloadTask
UploadTask
以及断点续传、后台下载
所有的任务都是我们的Session发起的
默认所有的任务都是挂起的,需要我们resume
第一次resume就是Start
所有的任务都可以挂起

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    // 1. url
    NSURL *url = [NSURL URLWithString:@"http://www.weather.com.cn/data/sk/101190408.html"];
    [self taskWithUrl:url];
}

#pragma mark -- 开始一个请求
- (void)taskWithUrl:(NSURL *)url{
    // 2.苹果提供了一个全局的单例
    NSURLSession *session = [NSURLSession sharedSession];
    //    NSURLRequest *request = [NSURLRequest requestWithURL:url];    // 可以省略这一步 让task内部自动创建
    // 3.发送我们的网络任务
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        id result = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
        NSLog(@"%@",result);
    }];
    // 4.启动任务
    [dataTask resume];
}
Paste_Image.png
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSURL *url = [NSURL URLWithString:@"http://v.ku6.com/playlistVideo.htm?t=list&p=1&playlistId=2196066"];
    // 使用下载方法下载
    /*
     如果在回调方法中,不做任何处理,下载的文件会被删除
     下载文件放在temp里面,系统会自动回收这块区域!
     设计的目的是什么?
     - 通常从网络上下载文件,什么格式最多?-》zip文件最多!可以替代用户节约流量!!
     - 如果是zip包,下载之后,需要解压
     - 解压缩之后,原始的zip文件就不再需要了,系统会自动帮助我们删掉!
     */
    [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@",location);
    }];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSURL *url = [NSURL URLWithString:@""];
    
    [[[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        // 下载结束 解压缩到目标路径
        // 目标路径
        NSString *cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject;
        // 只需要知道目标路径,不需要知道目标文件
        // 因为一个压缩包中有可能有多个文件
        [SSZipArchive unzipFileAtPath:location.path toDestination:cachePath];
    }] resume];
}

下面 来搞一个断点续传

#import <UIKit/UIKit.h>

@interface JYFProgressButton : UIButton

/**
 进度
 */
@property (nonatomic, assign) float progress;

@end
#import "JYFProgressButton.h"

@implementation JYFProgressButton

- (void)setProgress:(float)progress{
    _progress = progress;
    
    // 设置Title
    [self setTitle:[NSString stringWithFormat:@"%.02f%%",_progress * 100] forState:(UIControlStateNormal)];
    
    // 刷图视图 会调用drawRect
    [self setNeedsDisplay];
}

/*
 写一个宏 myMIN(a,b) 返回最小值
 写一个宏 myMIN3(a,b,c) 返回最小值
 */

#define myMIN(a,b) (((a) < (b))?(a):(b))
#define myMIN3(a,b,c) myMIN(myMIN(a,b,),c)

#pragma mark -- 画个圆
- (void)drawRect:(CGRect)rect{
    
    // 圆心
    CGPoint center = CGPointMake(rect.size.width / 2, rect.size.height / 2);
    // 半径
//    CGFloat r = (rect.size.height > rect.size.width)? rect.size.width * 0.4 : rect.size.height * 0.4;
    CGFloat r = myMIN(rect.size.width, rect.size.height) * 0.4;
    // 从什么地方开始
    CGFloat startAng = - M_PI_2; // 从180度开始
    // 到什么地方结束
    CGFloat endAng = self.progress * 2 * M_PI + startAng; // 两倍的PI就是一个圆
    /*
     1.圆心
     2.半径
     3.起始角度
     4.结束角度
     5.顺时针
     */
    // 使用贝塞尔曲线来进行绘制
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:r startAngle:startAng endAngle:endAng clockwise:YES];
    
    // 设置线条宽度
    path.lineWidth = 10;
    // 设置线条风格
    path.lineCapStyle = kCGLineCapRound;
    // 填充颜色
    [[UIColor blueColor] setStroke];
    // 绘制路径
    [path stroke];
}
#import "ViewController.h"
#import "JYFProgressButton.h"

@interface ViewController ()<NSURLSessionDownloadDelegate>

// 管理全局的session任务
@property (nonatomic, strong) NSURLSession *session;

@property (weak, nonatomic) IBOutlet JYFProgressButton *progressView;

/**
 全局的下载任务
 */
@property (nonatomic, strong) NSURLSessionDownloadTask *downloadTask;

/**
 暂停的时候已经下载的数据
 */
@property (nonatomic, strong) NSData *resumeData;

@end

@implementation ViewController

- (NSURLSession *)session{
    if (!_session) {
        // config 提供了一个全局的网络环境配置 包括身份验证 浏览器类型 cookie 缓存 超时
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        // 设置代理的队列
        /*
         队列:设置回调的代理方法在哪里执行 这里只是应用于回调 那么下载一定是在异步执行的
             - 代理的队列 如果给 nil 在多个线程中执行时没有问题的
             - [NSOperationQueue mainQueue] 主队列 可以给nil 默认子线程
         Session 会对代理强引用 如果任务结束后 不取消Session 会出现内存泄露
         // 真正的网络访问
         - 在网络开发中,应该将所有的网络访问操作,封装到一个方法中,由一个统一的单例对象负责所有的网络事件
         - Session对代理(单例)进行强引用!单例本身就是一个静态的实例,本身不需要释放
         - AFN -> 需要建立一个AFN 的Manager
         */
        
        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark -- NSURLSession Delegate
#pragma mark -- 下载完成 7.0以下三个方法  都要实现,但是8.0就不需要了 只需要实现下载完成就好了
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location{
    // 任务完成
    [self.session finishTasksAndInvalidate]; // 只是内部干掉了代理 并没有清空
    // 清空我们的Session 为了让它下载懒加载的时候重新初始化
    self.session = nil;
    NSLog(@"%@",location);
}

#pragma mark -- 下载续传
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes{
    
}

#pragma mark -- 下载进度监听的方法
/*
 1.session
 2.downloadTask 调用代理方式的下载任务
 3.bytesWritten 本次下载的字节数
 4.totalBytesWritten 已经下载的字节数
 5.totalBytesExpectedToWrite 期望下载的总大小
 */
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite{
    
    // 下载进度
    float progress = (float) totalBytesWritten / totalBytesExpectedToWrite;
    NSLog(@"%f",progress);
    
    // 打印当前线程
    NSLog(@"%f %@",progress,[NSThread currentThread]);
    
    // 回到主线程更新UI
    dispatch_async(dispatch_get_main_queue(), ^{
        self.progressView.progress = progress;
    });
}

#pragma mark -- 开始下载
- (IBAction)start:(UIButton *)sender {
    NSURL *url = [NSURL URLWithString:@"http://dldir1.qq.com/qqfile/QQforMac/QQ_V5.4.1.dmg"];
    // 使用下载方法下载
    // 如果需要下载进度,也需要通过监听 NSURLSession和NSURLConnection一样都是通过代理!!
    // NSURLSession 它的全局的 Session 单例是整个系统的
    // NSURLSession是全局的单例 是整个系统的 那么我们设置代理不能全局设置
    /*
     如果需要跟进下载进度,不能使用快代码回调的方式
     如果使用Block 默认是子线程
     所以 要回到主线程
     */
    //    [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    //        NSLog(@"%@",location);
    //    }];
    // 需要使用代理的方式
//    [[self.session downloadTaskWithURL:url] resume];
    
    // 断点续传 需要使用全局的downloadTask 下载
    self.downloadTask = [self.session downloadTaskWithURL:url];
    // 开始任务
    self.resumeData = nil;
    [self.downloadTask cancel];
    [self.downloadTask resume];
}

#pragma mark -- 暂停下载
- (IBAction)pause:(UIButton *)sender {
    
    // 暂停 肯定是暂停任务
    [self.downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
        // resumeData : 续传的数据(下载了多少)
        NSLog(@"数据的长度:%tu",resumeData.length);
        
        self.resumeData = resumeData;
        // 释放下载任务 我们task设置为weak 就可以不用释放了 因为我们的任务都是由Session发起的 而Session对发起的任务都会持有一个Strong 都会持有一个强引用 如果不手动释放 非常危险
        // 解决Session强引用的问题 就必须让Session释放掉
        self.downloadTask = nil;
    }];
}

#pragma mark -- 继续下载
- (IBAction)resume:(UIButton *)sender {
    
    NSLog(@"%ld",(long)self.downloadTask.state);
    if (self.resumeData == nil) {
        return;
    }
    // 继续下载 继续下载任务 也是应该由Session发起的
    // 使用续传数据启动下载任务
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];
    // 清空续传数据
    self.resumeData = nil;
    // 所有的任务都是默认挂起的
    [self.downloadTask resume];
}

- (void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    
    // 取消会话
    [self.session invalidateAndCancel];
    self.session = nil;
}

@end
URLSession.gif
上一篇下一篇

猜你喜欢

热点阅读