OC-实现下载单例类

文章目录

前言

在第一个项目中,需要实现歌曲下载的控制,简单实现如下

下载类

objc 复制代码
@property (nonatomic, strong)NSURLSession* session;//这个session是负责会话的工厂、任务的调度中心、网络回调的分发中心,在这里将所有的网络事件交给manager来处理
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSURLSessionDownloadTask *> *downloadTasks;//保存当前正在下载的NSURLSessionDownloadTask实例,便于实现暂停、取查询下载状态,这里每个任务都是一个网络请求
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSData *> *resumeDataMap//保存下载的断点续传数据,在iOS中,提供了cancleByProducingResumeData:接口,会生成一个二进制数据,内部包含已经下载文件内容、文件的下载进度、HTTP请求状态信息

初始化

objc 复制代码
+ (instancetype)sharedManager {
  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    manager = [[MusicDownloadManager alloc] init];
    manager.resumeDataMap = [NSMutableDictionary dictionary];
    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];//获取一个默认的网络会话配置
    manager.session = [NSURLSession sessionWithConfiguration:config delegate:manager delegateQueue:[[NSOperationQueue alloc] init]];//所有delegate的回调都在主线程操作,默认是在后台线程
    manager.downloadTasks = [NSMutableDictionary dictionary];
  });
  return manager;
}

下载实现

objc 复制代码
- (void)downloadSongWithURL:(NSURL* )url progress:(void(^)(float))progressBlock completion:(void(^)(NSURL* , NSError* ))completionBlock {
  NSString* key = url.absoluteString;//获取url的绝对路径,这里如果使用url当作字典的键的话,url的比对会调用hash/equal方法来对比,之接用作键的话可能会出现错误
  NSString* fileName = url.lastPathComponent;
  NSString* docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:fileName];//参数YES:表示返回绝对路径
  if([[NSFileManager defaultManager] fileExistsAtPath:docPath]) {
    if (completionBlock) {
      completionBlock([NSURL fileURLWithPath:docPath], nil);
    }
    return;
  }//获取iOS提供的文件管理类,如果文件存在说明文件已经下载过,返回文件路径就行,不必在下载一次
	NSURLSessionDownloadTask* task = nil;
  NSData* resumeData = self.resumeDataMa[key];
  if (resumeData) {
		task = [self.session downloadTaskWithResumeData:resumeData];
    [self.resumeDataMap removeObjectForKey:key];
  } else {
    task = [self.session downloadTaskWithURL:url];
	}//查询resumeDataMap是否有未完成下载的任务,如果有就继续传输
  //内部调用CFNetwork处理请求,TCP/IP,文件写入部分工作
  objc_setAssociationObject(task, "progressBlock", progressBlock, OBJC_ASSOCIATION_COPY_NSNATOMIC);//每次下载进度回调
    objc_setAssociationObject(task, "completionBlock", progressBlock, OBJC_ASSOCIATION_COPY_NSNATOMIC);//下载完成或失败回调
}

取消下载

objc 复制代码
- (void)cancelDownloadForURL:(NSURL *)url {
  NSString* key = url.absoluteString;
  NSURLSessionDownloadTask *task = self.downloadTasks[key];
  if (task) {
    [task cancel];
    [self.downloadTasks removeObjectForKey:key];
    [self.resumeDataMap removeObjectForKey:key];
  }
}

暂停下载

objc 复制代码
- (void)pauseDownloadForURL:(NSURL *)url {
  NSString* key = url.absoluteString;
  NSURLSessionDownloadTask *task = self.downloadTasks[key];
  if (!task) {
 		return;
  }
  [task cancelByProducingResumeData:^(NSData *resumeData) {//暂停并获取断点数据
    if (resumeData) {
      self.resumeDataMap[key] = resumeData;
    }
    [self.downloadTasks removeObjectForKey:key];
  }];
}

监听下载进度

objc 复制代码
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
  NSLog(@"正在下载");
  void(^progressBlock)(float) = objc_getAssociatedObject(downloadTask, "progressBlock");//将回调block取出
  if (!progressBlock) {
    return;
  }
  if (totalBytesExpectedToWrite > 0) {
    float progress = (float)totalBytesWritten / totalBytesExpectedToWrite;
    progressBlock(progress);
  }
}

实时监听下载任务的数据写入情况,当下载任务向本地临时文件写入部分数据时系统就会调用这个方法

objc 复制代码
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
  NSString *fileName = downloadTask.originalRequest.URL.lastPathComponent;
  NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:fileName];
  NSFileManager *fm = [NSFileManager defaultManager];

  NSError *error = nil;
  BOOL success = [fm moveItemAtURL:location toURL:[NSURL fileURLWithPath:docPath] error:&error];//将临时文件移动到docPath

  // 回调 completion,通知下载完成或者失败
  void(^completionBlock)(NSURL *, NSError *) = objc_getAssociatedObject(downloadTask, "completionBlock");

  if (completionBlock) {
    completionBlock(success ? [NSURL fileURLWithPath:docPath] : nil, error);
  }

  // 移除任务
  NSString *key = downloadTask.originalRequest.URL.absoluteString;
  [self.downloadTasks removeObjectForKey:key];
  [self.resumeDataMap removeObjectForKey:key];
}
相关推荐
人月神话-Lee11 小时前
【图像处理】卷积原理与卷积核——图像处理的核心引擎
图像处理·深度学习·ios·ai编程·swift
2501_9151063213 小时前
深入解析无源码iOS加固原理与方案,保护应用安全
android·安全·ios·小程序·uni-app·cocoa·iphone
Daniel_Coder14 小时前
iOS Widget 开发-15:Widget 性能优化指南
ios·swift·widget·widgetcenter
磊 子16 小时前
C++ IO 流
macos·objective-c·cocoa
库奇噜啦呼16 小时前
【iOS】源码学习-dyld加载
学习·ios·cocoa
Daniel_Coder17 小时前
iOS Widget 开发-16:Widget 网络数据加载策略
ios·swift·widget·widgetcenter
美狐美颜SDK开放平台17 小时前
美颜SDK开发详解:如何优化美颜SDK在低端安卓机上的性能?
android·ios·音视频·直播美颜sdk·视频美颜sdk
Kurisu57517 小时前
FilzaCracked_4.0.0_TS.ipa2026最新官方正版免费下载 一键转存 永久更新 (看到速转存 资源随时走丢)手机版通用
ios·智能手机·电脑·巨魔
ACP广源盛139246256731 天前
iOS 27 开放 AI 生态@ACP#小型化扩展黄金风口,IX8008全面超越 ASM2806,铸就嵌入式 AI 扩展核心
人工智能·嵌入式硬件·macos·ios·计算机外设·objective-c·cocoa
人月神话Lee1 天前
【图像处理】卷积原理与卷积核——图像处理的核心引擎
ios·ai编程·图像识别