系列文章:
在上一章節(jié)中我們通過(guò)一個(gè)具體的實(shí)例講解了AF是如何處理一下網(wǎng)絡(luò)請(qǐng)求的,本章將通過(guò)下載的實(shí)例(上傳的實(shí)現(xiàn)類似不再做額外分析)作為入口,再做進(jìn)一步分析。
1.NSURLSessionDownloadTask
AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://dn-arnold.qbox.me/Snip.zip"]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:10];
self.downloadTask = [session downloadTaskWithRequest:request
progress:^(NSProgress * _Nonnull downloadProgress) {
CGFloat progress = 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount;
NSLog(@"下載進(jìn)度 :%.2f",progress);
}
destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
// 下載文件存儲(chǔ)的路徑
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
path = [path stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"下載路徑 :%@",path);
return [NSURL fileURLWithPath:path];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if(!error){
NSLog(@"下載完成: %@",[filePath path]);
}
}];
[_downloadTask resume];
1.1 初始化task
// 1.根據(jù)請(qǐng)求獲取下載的task
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
// 2.將傳入的下載路徑的block存儲(chǔ)到代理實(shí)例中
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
// 3.將下載進(jìn)度的block也存儲(chǔ)到代理對(duì)象中
delegate.downloadProgressBlock = downloadProgressBlock;
1.2 下載完成
AFURLSessionManager.m
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
if (self.downloadTaskDidFinishDownloading) {
NSURL *fileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (fileURL) {
delegate.downloadFileURL = fileURL;
NSError *error = nil;
[[NSFileManager defaultManager] moveItemAtURL:location toURL:fileURL error:&error];
if (error) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:error.userInfo];
}
return;
}
}
// task綁定的deledate也執(zhí)行共有的數(shù)據(jù)處理方法
if (delegate) {
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}
}
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSError *fileManagerError = nil;
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
if (fileManagerError) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
上述的兩個(gè)實(shí)現(xiàn),達(dá)到的目的是一樣的:獲取下載存儲(chǔ)的路徑,將下載的文件move到傳入的下載的路徑。但是為什么需要實(shí)現(xiàn)兩遍?原來(lái)是AFURLSessionManager提供的對(duì)外的接口,可以將下載路徑的回調(diào)傳入到session中。
接口:
/**
Sets a block to be executed when a download task has completed a download, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didFinishDownloadingToURL:`.
@param block A block object to be executed when a download task has completed. The block returns the URL the download should be moved to, and takes three arguments: the session, the download task, and the temporary location of the downloaded file. If the file manager encounters an error while attempting to move the temporary file to the destination, an `AFURLSessionDownloadTaskDidFailToMoveFileNotification` will be posted, with the download task as its object, and the user info of the error.
*/
- (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
使用:
[session setDownloadTaskDidFinishDownloadingBlock:^NSURL * _Nullable(NSURLSession * _Nonnull session, NSURLSessionDownloadTask * _Nonnull downloadTask, NSURL * _Nonnull location) {
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
path = [path stringByAppendingPathComponent:@"result.zip"];
return [NSURL fileURLWithPath:path];
}];
1.3 下載進(jìn)度
session可以調(diào)用如下的接口傳入下載進(jìn)度與重啟下載的回調(diào),以便實(shí)現(xiàn)自己的可定制的邏輯。
/**
Sets a block to be executed periodically to track download progress, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:`.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes five arguments: the session, the download task, the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the session manager operation queue.
*/
- (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
/**
Sets a block to be executed when a download task has been resumed, as handled by the `NSURLSessionDownloadDelegate` method `URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:`.
@param block A block object to be executed when a download task has been resumed. The block has no return value and takes four arguments: the session, the download task, the file offset of the resumed download, and the total number of bytes expected to be downloaded.
*/
- (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
使用:
[_session setDownloadTaskDidWriteDataBlock:^(NSURLSession * _Nonnull session, NSURLSessionDownloadTask * _Nonnull downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite) {
NSLog(@"下載進(jìn)度 %lld/%lld",totalBytesWritten,totalBytesExpectedToWrite);
}];
[_session setDownloadTaskDidResumeBlock:^(NSURLSession * _Nonnull session, NSURLSessionDownloadTask * _Nonnull downloadTask, int64_t fileOffset, int64_t expectedTotalBytes) {
NSLog(@"重啟下載 %lld %lld",fileOffset,expectedTotalBytes);
}];
而AF是是如何處理下載進(jìn)度監(jiān)聽的呢?
(1)綁定下載進(jìn)度的回調(diào)
上述1.1 初始化task中:[self setDelegate:delegate forTask:downloadTask];實(shí)現(xiàn)了對(duì)下載進(jìn)度的監(jiān)聽,我們看看有關(guān)下載部分的具體代碼:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
...
[delegate setupProgressForTask:task];
...
}
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
[self.downloadProgress setCancellable:YES];
// 1.下載取消的回調(diào)
[self.downloadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
// 2.下載暫停的回調(diào)
[self.downloadProgress setPausable:YES];
[self.downloadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.downloadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
// 1.kvo的方式監(jiān)聽task countOfBytesReceived,countOfBytesExpectedToReceive屬性的變化。
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
// 2.kvo的方式監(jiān)聽fractionCompleted即下載進(jìn)度的變化。
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
(2)具體進(jìn)度變化的觸發(fā)
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
// 1.收到task countOfBytesReceived屬性變化時(shí)更新downloadProgress的下載完成的總數(shù)
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
// 2.收到task countOfBytesExpectedToReceive屬性變化時(shí)更新downloadProgress的下載總數(shù)
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
}
}
else if ([object isEqual:self.downloadProgress]) {
// 3.收到downloadProgressBlock fractionCompleted屬性變化時(shí)觸發(fā)上層傳入的下載進(jìn)度的回調(diào)
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
}
1.4 下載暫停,取消
在1.3 downloadProgress中我們看到了下載暫停與取消的回調(diào)的設(shè)置,我們看看如何觸發(fā)回調(diào),先看外部獲取task的downloadProgress實(shí)例的接口:
/**
Returns the download progress of the specified task.
@param task The session task. Must not be `nil`.
@return An `NSProgress` object reporting the download progress of a task, or `nil` if the progress is unavailable.
*/
- (nullable NSProgress *)downloadProgressForTask:(NSURLSessionTask *)task;
拿到了task對(duì)應(yīng)的downloadProgress的實(shí)例,就可以執(zhí)行cancel
,pause
的方法,觸發(fā)downloadProgress設(shè)置的回調(diào),進(jìn)而改變task的狀態(tài),具體調(diào)用實(shí)例:
[[session downloadProgressForTask:downloadTask] pause];
[[session downloadProgressForTask:downloadTask] cancel];
當(dāng)然也可以直接操作task:
[_downloadTask suspend];
[_downloadTask resume];
2.關(guān)于斷點(diǎn)續(xù)傳
網(wǎng)絡(luò)下載中難免不需要考慮斷點(diǎn)續(xù)傳的問題。
2.1 非退出程序的斷點(diǎn)續(xù)傳
這種需求,原生的Api支持,且同時(shí)也支持后臺(tái)下載,具體看demo代碼:
typedef void (^DownloadProgressBlock)(NSProgress *downloadProgress);
typedef NSURL* (^DownloadDestinationBlock)(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response);
typedef void (^DownloadCompletionBlock)(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error);
- (void)initUI{
UIButton *startButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
startButton.center = CGPointMake(self.view.center.x, 100);
startButton.backgroundColor = [UIColor redColor];
[startButton setTitle:@"開始" forState:UIControlStateNormal];
[startButton addTarget:self action:@selector(start:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:startButton];
UIButton *pauseButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
pauseButton.center = CGPointMake(self.view.center.x, 200);
pauseButton.backgroundColor = [UIColor redColor];
[pauseButton setTitle:@"停止" forState:UIControlStateNormal];
[pauseButton addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:pauseButton];
}
- (void)cancel:(id)sender{
NSLog(@"下載取消");
__weak typeof(self) weakSelf = self;
[_downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
weakSelf.resumeData = resumeData;
}];
}
- (void)start:(id)sender{
NSLog(@"下載開始");
DownloadProgressBlock downloadProgressBlock = ^(NSProgress * _Nonnull downloadProgress) {
CGFloat progress = 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount;
NSLog(@"下載進(jìn)度 :%.2f",progress);
};
DownloadDestinationBlock downloadDestinationBlock = ^NSURL*(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
// 下載文件存儲(chǔ)的路徑
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
path = [path stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"下載路徑 :%@",path);
return [NSURL fileURLWithPath:path];
};
DownloadCompletionBlock downloadCompletionBlock = ^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if(!error){
NSLog(@"下載完成: %@",[filePath path]);
}
};
if(_resumeData && _resumeData.length > 0){
self.downloadTask = [_session downloadTaskWithResumeData:_resumeData
progress:downloadProgressBlock
destination:downloadDestinationBlock
completionHandler:downloadCompletionBlock];
}else{
self.session = [AFHTTPSessionManager manager];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"https://dn-arnold.qbox.me/Dash.zip"]];
self.downloadTask = [_session downloadTaskWithRequest:request
progress:downloadProgressBlock
destination:downloadDestinationBlock
completionHandler:downloadCompletionBlock];
}
[_downloadTask resume];
}
運(yùn)行結(jié)果:
2017-06-03 17:38:02.678 InterView[46838:517522] 下載開始
2017-06-03 17:38:03.007 InterView[46838:517748] 下載進(jìn)度 :0.01
2017-06-03 17:38:03.070 InterView[46838:517738] 下載進(jìn)度 :0.01
2017-06-03 17:38:03.091 InterView[46838:517743] 下載進(jìn)度 :0.02
2017-06-03 17:38:03.139 InterView[46838:517738] 下載進(jìn)度 :0.02
2017-06-03 17:38:03.212 InterView[46838:517743] 下載進(jìn)度 :0.03
2017-06-03 17:38:03.277 InterView[46838:517743] 下載進(jìn)度 :0.03
2017-06-03 17:38:03.358 InterView[46838:517738] 下載進(jìn)度 :0.04
2017-06-03 17:38:03.461 InterView[46838:517738] 下載進(jìn)度 :0.04
2017-06-03 17:38:03.532 InterView[46838:517748] 下載進(jìn)度 :0.05
2017-06-03 17:38:03.552 InterView[46838:517522] 下載取消
2017-06-03 17:38:05.285 InterView[46838:517522] 下載開始
2017-06-03 17:38:05.637 InterView[46838:517738] 下載進(jìn)度 :0.05
2017-06-03 17:38:05.637 InterView[46838:517738] 下載進(jìn)度 :0.05
2017-06-03 17:38:05.638 InterView[46838:517738] 下載進(jìn)度 :0.05
2017-06-03 17:38:05.718 InterView[46838:517743] 下載進(jìn)度 :0.05
2017-06-03 17:38:05.742 InterView[46838:517801] 下載進(jìn)度 :0.06
2017-06-03 17:38:05.759 InterView[46838:517768] 下載進(jìn)度 :0.06
2017-06-03 17:38:05.804 InterView[46838:517748] 下載進(jìn)度 :0.06
2017-06-03 17:38:05.899 InterView[46838:517738] 下載進(jìn)度 :0.07
2017-06-03 17:38:05.964 InterView[46838:517738] 下載進(jìn)度 :0.07
2017-06-03 17:38:06.035 InterView[46838:517738] 下載進(jìn)度 :0.08
2017-06-03 17:38:06.106 InterView[46838:517743] 下載進(jìn)度 :0.08
2017-06-03 17:38:06.172 InterView[46838:517801] 下載進(jìn)度 :0.09
2017-06-03 17:38:06.236 InterView[46838:517768] 下載進(jìn)度 :0.09
2017-06-03 17:38:06.300 InterView[46838:517522] 下載取消
2.2 退出程序的斷點(diǎn)續(xù)傳
具體的業(yè)務(wù)中存在用戶退出程序,重啟進(jìn)入App依舊下載任務(wù)依舊能斷點(diǎn)續(xù)傳,如果需要實(shí)現(xiàn)這種場(chǎng)景就必須存在臨時(shí)的文件存儲(chǔ),以便重啟讀取數(shù)據(jù),查看了相關(guān)的Api目前尚未找到實(shí)現(xiàn)方式。查看了網(wǎng)上的資料參考了:使用NSURLSession程序退出后繼續(xù)下載中的的思想實(shí)現(xiàn)了這一需求,寫了一份Demo代碼測(cè)試一下:
- (void)cancel:(id)sender{
NSLog(@"下載取消");
[_downloadTask cancel];
}
- (void)start:(id)sender{
NSLog(@"下載開始");
DownloadProgressBlock downloadProgressBlock = ^(NSProgress * _Nonnull downloadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
CGFloat progress = 1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount;
NSString *progressString = [NSString stringWithFormat:@"下載進(jìn)度 :%.2f",progress];
UILabel *progressLabel = [self.view viewWithTag:1025];
progressLabel.text = progressString;
});
};
DownloadDestinationBlock downloadDestinationBlock = ^NSURL*(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
// 下載文件存儲(chǔ)的路徑
NSString * path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
path = [path stringByAppendingPathComponent:response.suggestedFilename];
NSLog(@"下載路徑 :%@",path);
return [NSURL fileURLWithPath:path];
};
DownloadCompletionBlock downloadCompletionBlock = ^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
if(!error){
[[NSUserDefaults standardUserDefaults] removeObjectForKey:_currentDownloadUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(@"下載完成: %@",[filePath path]);
}
};
self.currentDownloadUrl = @"https://dn-arnold.qbox.me/Dash.zip";
self.session = [AFHTTPSessionManager manager];
NSData *resumeData;
NSString *resumeDataFilePath = [[NSUserDefaults standardUserDefaults] objectForKey:_currentDownloadUrl];
if(resumeDataFilePath && resumeDataFilePath.length > 0){
if([[NSFileManager defaultManager] fileExistsAtPath:resumeDataFilePath]){
resumeData = [self resumeDataFromFilePath:resumeDataFilePath];
}
}
if(resumeData && resumeData.length > 0){
self.downloadTask = [_session downloadTaskWithResumeData:resumeData
progress:downloadProgressBlock
destination:downloadDestinationBlock
completionHandler:downloadCompletionBlock];
}else{
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:_currentDownloadUrl]];
self.downloadTask = [_session downloadTaskWithRequest:request
progress:downloadProgressBlock
destination:downloadDestinationBlock
completionHandler:downloadCompletionBlock];
resumeDataFilePath = [self resumeDataFilePathFor:_downloadTask
downloadUrl:_currentDownloadUrl];
[[NSUserDefaults standardUserDefaults] setObject:resumeDataFilePath
forKey:_currentDownloadUrl];
[[NSUserDefaults standardUserDefaults] synchronize];
}
[_downloadTask resume];
}
// 拼接斷點(diǎn)續(xù)傳初始化傳入的resumeData數(shù)據(jù)
- (NSData *)resumeDataFromFilePath:(NSString *)filePath{
NSData *resumeData=[[NSData alloc] initWithContentsOfFile:filePath];
if(resumeData && resumeData.length>0){
NSMutableDictionary *resumeDataDict = [NSMutableDictionary dictionary];
NSMutableURLRequest *newResumeRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:_currentDownloadUrl]];
[newResumeRequest addValue:[NSString stringWithFormat:@"bytes=%ld-",resumeData.length] forHTTPHeaderField:@"Range"];
NSData *newResumeRequestData = [NSKeyedArchiver archivedDataWithRootObject:newResumeRequest];
[resumeDataDict setObject:_currentDownloadUrl
forKey:@"NSURLSessionDownloadURL"]; // 需要這一句不然會(huì)報(bào)錯(cuò)Code=-1002 "unsupported URL"
[resumeDataDict setObject:[NSNumber numberWithInteger:resumeData.length]
forKey:@"NSURLSessionResumeBytesReceived"];
[resumeDataDict setObject:newResumeRequestData
forKey:@"NSURLSessionResumeCurrentRequest"];
[resumeDataDict setObject:[filePath lastPathComponent]
forKey:@"NSURLSessionResumeInfoTempFileName"];
NSData *resumeData = [NSPropertyListSerialization
dataWithPropertyList:resumeDataDict
format:NSPropertyListBinaryFormat_v1_0
options:0
error:nil];
return resumeData;
}
return nil;
}
// 利用runtime的方式根據(jù)task獲取相應(yīng)的臨時(shí)下載的目錄
- (NSString *)resumeDataFilePathFor:(NSURLSessionDownloadTask *)downloadTask
downloadUrl:(NSString *)downloadUrl{
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([downloadTask class], &outCount);
for (i = 0; i<outCount; i++) {
objc_property_t property = properties[i];
const char* char_f =property_getName(property);
NSString *propertyName = [NSString stringWithUTF8String:char_f];
if ([@"downloadFile" isEqualToString:propertyName]) {
id propertyValue = [downloadTask valueForKey:(NSString *)propertyName];
unsigned int downloadFileoutCount, downloadFileIndex;
objc_property_t *downloadFileproperties = class_copyPropertyList([propertyValue class], &downloadFileoutCount);
for (downloadFileIndex = 0; downloadFileIndex < downloadFileoutCount; downloadFileIndex++) {
objc_property_t downloadFileproperty = downloadFileproperties[downloadFileIndex];
const char* downloadFilechar_f =property_getName(downloadFileproperty);
NSString *downloadFilepropertyName = [NSString stringWithUTF8String:downloadFilechar_f];
if([@"path" isEqualToString:downloadFilepropertyName]){
id downloadFilepropertyValue = [propertyValue valueForKey:(NSString *)downloadFilepropertyName];
if(downloadFilepropertyValue){
return downloadFilepropertyValue;
}
break;
}
}
free(downloadFileproperties);
}else {
continue;
}
}
free(properties);
return nil;
}
上述代碼實(shí)現(xiàn)比較粗糙,只是實(shí)現(xiàn)核心內(nèi)容,demo測(cè)試基本達(dá)到了效果。
存在問題
- 因?yàn)槭莾?nèi)部實(shí)現(xiàn),蘋果一旦替換了實(shí)現(xiàn)方式,或者更改了task的部分屬性名,兼容性會(huì)是問題。