@@ -137,15 +137,15 @@ static dispatch_group_t url_session_manager_completion_group() {
137
137
138
138
139
139
/* *
140
- NSURLSessionDataTask //一般的get、post等请求
141
- NSURLSessionUploadTask // 用于上传文件或者数据量比较大的请求
142
- NSURLSessionDownloadTask //用于下载文件或者数据量比较大的请求
143
- NSURLSessionStreamTask //建立一个TCP / IP连接的主机名和端口或一个网络服务对象。
140
+ NSURLSessionDataTask // 一般的get、post等请求----请求得到数据存放在内存中
141
+ NSURLSessionUploadTask // 用于上传文件或者数据量比较大的请求 -----可以上传本地文件、流
142
+ NSURLSessionDownloadTask // 用于下载文件或者数据量比较大的请求-----下载的文件会临时写入沙盒
143
+ NSURLSessionStreamTask // 建立一个TCP/ IP连接的主机名和端口或一个网络服务对象。
144
144
145
145
*/
146
146
147
147
#pragma mark -
148
-
148
+ // 所有的task都有此代理来处理--所以遵循NSURLSessionTaskDelegate、NSURLSessionDataDelegate、NSURLSessionDownloadDelegate等不同类型task的代理方法
149
149
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate , NSURLSessionDataDelegate , NSURLSessionDownloadDelegate >
150
150
- (instancetype )initWithTask : (NSURLSessionTask *)task ;
151
151
@property (nonatomic , weak ) AFURLSessionManager *manager;
@@ -168,30 +168,34 @@ - (instancetype)initWithTask:(NSURLSessionTask *)task {
168
168
}
169
169
170
170
_mutableData = [NSMutableData data ];
171
+
172
+ // MARK:<NSProgress 需要深入了解这个类>
171
173
_uploadProgress = [[NSProgress alloc ] initWithParent: nil userInfo: nil ];
172
174
_downloadProgress = [[NSProgress alloc ] initWithParent: nil userInfo: nil ];
173
175
174
176
__weak __typeof__ (task) weakTask = task;
177
+ // 配置_uploadProgress、_downloadProgress
175
178
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
176
179
{
177
180
progress.totalUnitCount = NSURLSessionTransferSizeUnknown ;
178
181
progress.cancellable = YES ;
179
- progress.cancellationHandler = ^{
182
+ progress.cancellationHandler = ^{ // 取消的回调--取消任务
180
183
[weakTask cancel ];
181
184
};
182
185
progress.pausable = YES ;
183
- progress.pausingHandler = ^{
186
+ progress.pausingHandler = ^{ // 暂停回调--任务挂起
184
187
[weakTask suspend ];
185
188
};
189
+ // setResumingHandler: 该方法iOS9之后可以使用
186
190
if ([progress respondsToSelector: @selector (setResumingHandler: )]) {
187
191
progress.resumingHandler = ^{
188
- [weakTask resume ];
192
+ [weakTask resume ]; // 任务恢复
189
193
};
190
194
}
191
195
[progress addObserver: self
192
196
forKeyPath: NSStringFromSelector (@selector (fractionCompleted ))
193
197
options: NSKeyValueObservingOptionNew
194
- context: NULL ];
198
+ context: NULL ]; // 监听完成进度
195
199
}
196
200
return self;
197
201
}
@@ -202,15 +206,17 @@ - (void)dealloc {
202
206
}
203
207
204
208
#pragma mark - NSProgress Tracking
205
-
209
+ // downloadProgress、uploadProgress进度更新回调(KVO)
206
210
- (void )observeValueForKeyPath : (NSString *)keyPath ofObject : (id )object change : (NSDictionary <NSString *,id> *)change context : (void *)context {
207
211
if ([object isEqual: self .downloadProgress]) {
208
212
if (self.downloadProgressBlock ) {
213
+ // 使用者传入的下载进度回调
209
214
self.downloadProgressBlock (object);
210
215
}
211
216
}
212
217
else if ([object isEqual: self .uploadProgress]) {
213
218
if (self.uploadProgressBlock ) {
219
+ // 使用者传入的上传进度回调
214
220
self.uploadProgressBlock (object);
215
221
}
216
222
}
@@ -233,7 +239,7 @@ - (void)URLSession:(__unused NSURLSession *)session
233
239
{
234
240
// 所有的指针变量默认都是__strong类型,我们通常省略不写__strong
235
241
// delegate中的AFURLSessionManager使用weak方式引用 这里使用__strong 防止被释放
236
- // 1)强引用self.manager,防止被提前释放;因为self.manager声明为weak,类似Block
242
+ // 1)强引用self.manager,防止被提前释放;因为self.manager声明为weak
237
243
__strong AFURLSessionManager *manager = self.manager ;
238
244
239
245
__block id responseObject = nil ;
@@ -394,7 +400,7 @@ - (void)URLSession:(NSURLSession *)session
394
400
{
395
401
self.downloadFileURL = nil ;
396
402
// AFSessionManager也有个downloadTaskDidFinishDownloading 1750行
397
- // 这个是 “AFURLSessionManagerTaskDelegate”自定义的block (内部是用户自定义的block 看 900 行)
403
+ // 此处的downloadTaskDidFinishDownloading是 “AFURLSessionManagerTaskDelegate”的一个属性block (内部是用户自定义的block 看 900 行)
398
404
// 这个是对应的每个task的,每个task可以设置各自下载路径
399
405
if (self.downloadTaskDidFinishDownloading ) {
400
406
// 得到用户自定义下载路径
@@ -418,7 +424,7 @@ - (void)URLSession:(NSURLSession *)session
418
424
/* *
419
425
a.之前的NSURLSession的代理和这里的AFURLSessionManagerTaskDelegate都移动了文件到下载路径,而NSUrlSession代理的下载路径是所有request公用的下载路径,一旦设置,所有的request都会下载到之前那个路径。
420
426
421
- b.而这个是对应的每个task的,每个task可以设置各自下载路径 ??
427
+ b.而这个是对应的每个task的,每个task可以设置各自下载路径
422
428
解析 需要看在AFHTTPSessionManager中的一个方法
423
429
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
424
430
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
@@ -713,7 +719,6 @@ - (instancetype)init {
713
719
return [self initWithSessionConfiguration: nil ];
714
720
}
715
721
716
-
717
722
// AF3.x最最核心的类AFURLSessionManager。几乎所有的类都是围绕着这个类在处理业务逻辑。
718
723
- (instancetype )initWithSessionConfiguration : (NSURLSessionConfiguration *)configuration {
719
724
self = [super init ];
@@ -729,9 +734,10 @@ - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)config
729
734
730
735
self.operationQueue = [[NSOperationQueue alloc ] init ];
731
736
732
- // 这个operationQueue就是我们代理回调的queue。这里把代理回调的线程并发数设置为1了。至于这里为什么要这么做,我们先留一个坑,等我们讲完AF2.x之后再来分析这一块。
733
- self.operationQueue .maxConcurrentOperationCount = 1 ;
737
+ // TODO: <这个operationQueue就是我们代理回调的queue。这里把代理回调的线程并发数设置为1了。至于这里为什么要这么做,我们先留一个坑,等我们讲完AF2.x之后再来分析这一块>
734
738
739
+ self.operationQueue .maxConcurrentOperationCount = 1 ;
740
+
735
741
736
742
/* *
737
743
+ (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
@@ -740,7 +746,7 @@ + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configur
740
746
// 注意代理,代理的继承,实际上NSURLSession去判断了,你实现了哪个方法会去调用,包括子代理的方法!
741
747
self.session = [NSURLSession sessionWithConfiguration: self .sessionConfiguration delegate: self delegateQueue: self .operationQueue];
742
748
743
- // 各种响应转码
749
+ // 各种响应转码 AFJSONResponseSerializer--AFHTTPResponseSerializer<AFURLResponseSerialization>
744
750
self.responseSerializer = [AFJSONResponseSerializer serializer ];
745
751
// 设置默认安全策略
746
752
self.securityPolicy = [AFSecurityPolicy defaultPolicy ];
@@ -749,33 +755,59 @@ + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configur
749
755
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager ];
750
756
#endif
751
757
// 设置存储NSURL task与AFURLSessionManagerTaskDelegate的词典(重点,在AFNet中,每一个task都会被匹配一个AFURLSessionManagerTaskDelegate 来做task的delegate事件处理)
752
- // 用来让每一个请求task和我们自定义的AF代理来建立映射用的,其实AF对task的代理进行了一个封装,并且转发代理到AF自定义的代理,这是AF比较重要的一部分,接下来我们会具体讲这一块
758
+ // 用来让每一个请求task和我们自定义的AF代理来建立映射用的,其实AF对task的代理进行了一个封装,并且转发代理到AF自定义的代理,这是AF比较重要的一部分
753
759
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc ] init ];
754
760
755
761
// 设置AFURLSessionManagerTaskDelegate 词典的锁,确保词典在多线程访问时的线程安全
762
+
763
+ // MARK:<为什么需要使用‘锁’,需要有这个意识,没有意识的时候就多思考为什么代码要这么写,不这么写会出现什么问题>
756
764
self.lock = [[NSLock alloc ] init ];
757
765
self.lock .name = AFURLSessionManagerLockName;
758
766
759
-
760
- /* * ???
761
-
767
+ // MARK:<关于后台下载的session还需要理解透彻>
768
+ /* * ?????????????? 关于后台下载的session 还是没理解到位 ??????????????
762
769
这个方法用来异步的获取当前session的所有未完成的task。其实讲道理来说在初始化中调用这个方法应该里面一个task都不会有。我们打断点去看,也确实如此,里面的数组都是空的。
763
770
但是想想也知道,AF大神不会把一段没用的代码放在这吧。辗转多处,终于从AF的issue中找到了结论:https://github.com/AFNetworking/AFNetworking/issues/3499 。
764
771
765
- 原来这是为了从后台回来 ,重新初始化session,防止一些之前的后台请求任务 ,导致程序的crash。
772
+ ----来这是为了从后台回来 ,重新初始化session----,防止一些之前的'后台请求任务' ,导致程序的crash。
766
773
ApplicationDelegate 的代理方法里边处理未完成的后台download session
767
774
‘restoring a session from the background’ 这句话我理解是,有用同一个identifier 创建session的情况,一旦这种情况出现,会返回同一个session ,就需要对task进行获取和处理了
768
-
769
- 关于session的文档
775
+
776
+ 关于session的文档
770
777
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html#//apple_ref/doc/uid/10000165i-CH2-SW42
771
778
*/
772
779
773
780
/* *
774
- Asynchronously calls a completion callback with all data, upload, and download tasks in a session.
775
- The returned arrays contain any tasks that you have created within the session, not including any tasks that have been invalidated after completing, failing, or being cancelled.
781
+
782
+ 可以结合这两篇内容去理解下
783
+ https://realm.io/news/gwendolyn-weston-ios-background-networking/
784
+
785
+ http://stackoverflow.com/questions/23492311/nsurlsession-background-task-avoid-duplicates
786
+ // 这篇介绍了后台下载的
787
+ https://www.ralfebert.de/ios-examples/networking/urlsession-background-downloads/
788
+ // 一个基于URLSession的功能完善的下载器
789
+ https://www.jianshu.com/p/b4edfa0b71d8
790
+ Qusetions:
791
+
792
+ Is there a way to query NSURLSession to return a list of background tasks? It seems possible to make duplicate requests for background download tasks. I do not get any error if I make the same url download request while a previous one is still in progress.
793
+
794
+ What is the best way to handle this situation? I can maintain a list of urls I am currently downloading from, but in case the app is relaunched I lose this reference. I can again store this information in persistent storage. But it just seems inconvenient not to have an option to query NSURLSession for this.
776
795
796
+ Answer:
777
797
778
- 可以结合这两篇内容去理解下 https://realm.io/news/gwendolyn-weston-ios-background-networking/ http://stackoverflow.com/questions/23492311/nsurlsession-background-task-avoid-duplicates 在这里这个防御编程的作用是为了应对session在执行后台下载任务的情况。进入后台后在ApplicationDelegate那个方法中被唤醒,我们会去创建相同identifier的session,这个时候需要通过 getTasksWithCompletionHandler去获取未来得及执行的tasks去做一些防止崩溃的处理。
798
+ You could get the list of all download tasks added to the session with the following call.
799
+
800
+ [[self defaultSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
801
+ }];
802
+
803
+ 在这里这个防御编程的作用是为了应对session在执行后台下载任务的情况。进入后台后在ApplicationDelegate那个方法中被唤醒,我们会去创建相同identifier的session,这个时候需要通过 getTasksWithCompletionHandler去获取未来得及执行的tasks去做一些防止崩溃的处理。
804
+ */
805
+
806
+ /* *
807
+ getTasksWithCompletionHandler:
808
+ 获取后台没有完成的Tasks
809
+ Asynchronously calls a completion callback with all data, upload, and download tasks in a session.
810
+ The returned arrays contain any tasks that you have created within the session, not including any tasks that have been invalidated after completing, failing, or being cancelled.
779
811
*/
780
812
[self .session getTasksWithCompletionHandler: ^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
781
813
for (NSURLSessionDataTask *task in dataTasks) {
@@ -847,9 +879,11 @@ - (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
847
879
// 断言,如果没有这个参数,debug下crash在这
848
880
NSParameterAssert (task);
849
881
NSParameterAssert (delegate);
882
+ // MARK:<在操作NSDictionary时又出现了‘锁’>
850
883
// 加锁保证字典线程安全
851
884
[self .lock lock ];
852
885
// 将AFURLSessionManagerTaskDelegate放入以taskIdentifier标记的词典中(同一个NSURLSession中的taskIdentifier是唯一的)
886
+ // 代理和task建立映射关系
853
887
self.mutableTaskDelegatesKeyedByTaskIdentifier [@(task.taskIdentifier)] = delegate;
854
888
855
889
// 此版本有更改 版3.1.0此处还有个操作
@@ -875,11 +909,14 @@ - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
875
909
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc ] initWithTask: dataTask];
876
910
877
911
// AFURLSessionManagerTaskDelegate与AFURLSessionManager建立相互关系
912
+ // MARK:<manager属性是weak self不会被释放吗?>
878
913
delegate.manager = self;
879
914
880
915
delegate.completionHandler = completionHandler;
881
916
882
917
// 这个taskDescriptionForSessionTasks用来发送开始和挂起通知的时候会用到,就是用这个值来Post通知,来两者对应
918
+ // MARK:<这个taskDescription有什么作用?>
919
+
883
920
dataTask.taskDescription = self.taskDescriptionForSessionTasks ;
884
921
885
922
// ***** 将AFURLSessionManagerTaskDelegate对象与 dataTask建立关系
@@ -894,7 +931,7 @@ - (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
894
931
1)这个方法,生成了一个AFURLSessionManagerTaskDelegate,这个其实就是AF的自定义代理。我们请求传来的参数,都赋值给这个AF的代理了。
895
932
2)delegate.manager = self;代理把AFURLSessionManager这个类作为属性了,我们可以看到:
896
933
@property (nonatomic, weak) AFURLSessionManager *manager;
897
- 这个属性是弱引用的,所以不会存在循环引用的问题。
934
+ 这个属性是弱引用的,所以不会存在循环引用的问题。 (manage->delegate delegate-->manager)
898
935
899
936
3)我们调用了[self setDelegate:delegate forTask:dataTask];
900
937
*/
@@ -924,16 +961,16 @@ - (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
924
961
delegate.manager = self;
925
962
delegate.completionHandler = completionHandler;
926
963
927
- // 如果用户设置了destination这个block 看 380 行
964
+ // 如果用户设置了destination这个block(即用户设置了下载文件存放的路径) 看 380 行
928
965
if (destination) {
929
966
930
967
// 有点绕,就是把一个block赋值给“AF代理”的downloadTaskDidFinishDownloading,这个Block里的内部返回也是调用Block去获取到的,这里面的参数都是“AF代理”传来的。
931
968
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
932
- // 调用用户传入的destination这个block
969
+ // 调用用户传入的destination这个block (将下载文件的临时路径-location和响应信息传给用户-task.response)
933
970
return destination (location, task.response );
934
971
};
935
972
}
936
- /* *
973
+ /* * 下载文件临时存放在 ‘/tem’ 文件夹下
937
974
(lldb) po location
938
975
file:///private/var/mobile/Containers/Data/Application/CC27F214-F815-45AF-96D4-8EA0C5C1A215/tmp/CFNetworkDownload_iykDD1.tmp
939
976
0 commit comments