ios - 正确管理 NSURLSession 的建议

标签 ios asynchronous download nsurlsession background-thread

我开始开发一个 iphone 应用程序,我需要一些关于 NSURLSession 以及如何正确管理我的数据的下载和解析的建议。

我刚刚完成了在 nsurlsession 中下载我的数据的错误,但是关于我发现理解这些异步请求有多么困难,我认为我的解决方案并不是很好...... 下载错误也出现在 2 种不同的下载解决方案中,这让我觉得我忘了做某事...

在我的项目中,我下载了不同的 xml 文件(有时还有一些带有图片的 zip 文件),我需要在显示它们的信息之前对其进行解析。这些信息可能会快速变化,所以如果我再次加载我的页面,我想再次下载它们。 我一直在寻找一种简单的方法来以相同的方式管理所有下载,这样我就不必重新编写大量代码。

我找到了这个project首先。

有了它,我只需要使用该代码来管理下载:

NSString *downloadUrl = @"https://www.url.com";

        NSURL *location = [NSURL URLWithString:downloadUrl];
        // DownloadManager  is my version of the CTSessionOperation of the github project
        DownloadManager *operation = [DownloadManager new];
        operation.downloadUrl = downloadUrl;
        operation.completionAction = ^(NSURL *xmlUrl, BOOL success){
            dispatch_async(dispatch_get_main_queue(), ^{
                if (success){
                    regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]];
                    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:Nil waitUntilDone:YES];
                }
            });
        };
        operation.isBackground = YES;

       [operation enqueueOperation];

此代码在我第一次下载时完美运行。但是如果我尝试再次启动下载,下载不会启动(所以没有错误,只是,这个代码下载一次就这样)。

我通过修改 CTSessionOperation/DownloadManager 中的方法 (NSURLSession *)session 来纠正这个错误。我在评论中添加了“dispatch_once”以使其工作,但我认为这不是好的解决方案...

我尝试了导致相同错误的其他解决方案。我使用此代码管理下载:

     NSString *regionsUrl= @"url";

            NSURLSessionConfiguration *sessionConfig =
            [NSURLSessionConfiguration defaultSessionConfiguration];
// My solution to the bug          
/*NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration
                                                                  backgroundSessionConfiguration:[NSString stringWithFormat:@"com.captech.mysupersession.BackgroundSession%d",numBGSession]]; */
            //   numBGSession++; this is a static NSInteger


NSURLSession *session =
        [NSURLSession sessionWithConfiguration:backgroundConfiguration
                                      delegate:teamChoiceDetailViewController
                                 delegateQueue:nil];

        NSURLSessionDownloadTask *sessDLTask =
        [session downloadTaskWithURL:[NSURL URLWithString:regionsUrl]];

        [sessDLTask resume];

在委托(delegate)中:

-(void)URLSession:(NSURLSession *)session
     downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    dispatch_async(dispatch_get_main_queue(), ^{
        self.regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]];
        [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:Nil waitUntilDone:YES];
    });
}

使用此解决方案,我通过在每次尝试下载时创建自定义 NSURLSessionConfiguration 来避免错误。

所以我们开始吧。我对这两种解决方案感到很困惑。我不知道它们是否是管理下载的正确方法,我不认为我正确地纠正了错误,而且我一定是错过了 NSURLSession 的逻辑。

您对改进这些解决方案有什么建议吗?或者您认为其中一个比另一个好得多?

最佳答案

编辑:如果有人正在寻找更通用的解决方案来轻松正确地管理网络事务,您可以查看 AFNetworking它有一些魔力(另外你可以找到很多教程)。

我放弃了开发适用于每种情况的东西(特别是如果它是后台 session ,因为我不需要它)。 最后我刚刚创建了一个带有静态 session 的类,用于管理下载的委托(delegate),以及我在 didFinishDownloadingToURL 中使用的 block 来管理下载的数据。 当然不完美,但目前已经足够好了。

    typedef void (^CTCompletionBlock)(NSURL *location, NSError* err);

    @interface DownloadManager :  NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate>

    @property (nonatomic, retain) NSURLSessionDownloadTask *dlTask;
    @property (nonatomic, retain) NSString *location;
    @property (strong) CTCompletionBlock afterDLBlock;

    + (DownloadManager *)sharedManager;
    -(void)downloadTask;
    @end


    //
    //  DownloadManager.m
    //  MVCTest
    //
    //  Created by 
    //

    #import "DownloadManager.h"
    #import "AppDelegate.h"


    static DownloadManager *instance = nil;
    static NSURLSession *session = nil;

    @implementation DownloadManager

    + (DownloadManager *)sharedManager {
        if (instance == nil) {
            //session = [DownloadManager sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
            instance = [DownloadManager new];
        }
        return instance;
    }

    + (id)new
    {
        return [[self alloc] init];
    }
    - (id)init{
        self = [super init];
        session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
        return self;
    }

    -(void)downloadTask{
        self.dlTask = [session downloadTaskWithURL:[NSURL URLWithString:self.location]];
        [self.dlTask resume];
    }

    #pragma mark - NSURLSessionDownloadDelegate
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {

 NSError *error;
        if (self.afterDLBlock){
            self.afterDLBlock(location, error);
        }

    }

//i still have to manage the delegate...
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {}

    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {}

    #pragma mark - NSURLSessionTaskDelegate

    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {}

    -(void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error{}

    #pragma mark - NSURLSessionDelegate

    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {}

    @end

有了这个类,我只需要编写这段代码来管理下载的数据:

typeof(self) __weak weakSelf = self;
NSString *downloadUrl = @"http://www.whatyouwant.com";
[DownloadManager sharedManager].location = downloadUrl;
[DownloadManager sharedManager].afterDLBlock = ^(NSURL *location, NSError *error) {
        weakSelf.regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]];

        dispatch_sync(dispatch_get_main_queue(), ^{
            [weakSelf.activityViewIndicator stopAnimating];
            [weakSelf.tableView reloadData];
        });
};
[[DownloadManager sharedManager] downloadTask];

我仍然需要管理错误和委托(delegate),但是有了 taht 解决方案,我将不需要编写很多代码来下载一些数据

关于ios - 正确管理 NSURLSession 的建议,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22189299/

相关文章:

vba - 将 ADODB 二进制流转换为字符串 vba

ios - 从 super View 中删除 subview

javascript - 下载一个文件,片段存储在多个服务器上(HTTP)

ios - SwiftUI 中的 UserDefaults 与 Toggle 绑定(bind)

javascript - javascript for 循环内的异步过程

R Shiny 的进程队列

c# - 系统线程任务

java - 如何使用 sardine 从 webdav 服务器下载 zip 文件?

ios - 反复连续动画 View

ios - UICollectionView 布局,带有搜索栏的节标题和标题的另一个动态节标题