无法子类化URLSessionDataTask?如何为其添加关联属性?
关于子类化URLSessionDataTask的解决方案
核心结论:无法直接子类化URLSessionDataTask
苹果设计URLSession任务体系时,采用了封闭的工厂模式——所有URLSessionDataTask实例都是由URLSession内部创建返回的,开发者既不能直接实例化自定义子类,也无法让URLSession生成我们自己定义的子类实例。你写的URLSessionDataTask_Plus子类根本无法被URLSession调用生成实例,所以这条路走不通。
替代方案:用关联对象给系统任务动态附加属性
这是最接近你需求的实现方式,不需要额外包装类,直接给原生URLSessionDataTask添加存储进度观察的属性:
import ObjectiveC // 定义关联对象的唯一标识 private var ProgressObservationKey = 0 extension URLSessionDataTask { // 给系统任务动态添加progressObservation属性 var progressObservation: NSKeyValueObservation? { get { objc_getAssociatedObject(self, &ProgressObservationKey) as? NSKeyValueObservation } set { objc_setAssociatedObject(self, &ProgressObservationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } }
使用示例:
let url = URL(string: "https://example.com/large-file")! let task = URLSession.shared.dataTask(with: url) // 绑定进度观察并直接存在task对象上 task.progressObservation = task.progress.observe(\.fractionCompleted) { progress, _ in DispatchQueue.main.async { print("当前进度:\(progress.fractionCompleted * 100)%") } } task.resume()
进阶优化:封装任务创建逻辑
如果不想每次手动处理观察绑定,可以给URLSession写扩展,把逻辑封装起来:
extension URLSession { func dataTask(with url: URL, progressHandler: @escaping (Double) -> Void) -> URLSessionDataTask { let task = dataTask(with: url) let observation = task.progress.observe(\.fractionCompleted) { progress, _ in DispatchQueue.main.async { progressHandler(progress.fractionCompleted) } } task.progressObservation = observation return task } }
使用时更简洁:
let task = URLSession.shared.dataTask(with: url) { progress in print("进度更新:\(progress*100)%") } task.resume()
注意事项
- 关联对象会和task对象绑定生命周期:task被释放时,关联的
NSKeyValueObservation也会自动释放,无需手动管理销毁。 - 如果需要提前终止观察,直接设置
task.progressObservation = nil即可。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

