在iphone上同时下载后台

时间:2010-01-24 22:00:51

标签: iphone cocoa-touch

我正在尝试创建可以同时处理多个下载的类(我需要下载大量的小文件)并且我遇到“消失”连接的问题。

我有addDonwload函数,它将url添加到要下载的url列表中,并检查是否有可用的免费下载插槽。如果有,它立即开始下载。当其中一个下载完成后,我会选择第一个url表单列表并开始新的下载。

我使用NSURLConnection进行下载,这里有一些代码

- (bool) TryDownload:(downloadInfo*)info
{
    int index;
    @synchronized(_asyncConnection)
    {
        index = [_asyncConnection indexOfObject:nullObject];
        if(index != NSNotFound)
        {
            NSLog(@"downloading %@ at index %i", info.url, index);
            activeInfo[index] = info;
            NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];

            [_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]];
            //[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];           

            return true;
        }
    }

    return false;
}

- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
  [self performSelectorOnMainThread:@selector(DownloadFinished:) withObject:connection waitUntilDone:false];
}

- (void)DownloadFinished:(id)connection
{
    NSInteger index = NSNotFound;
    @synchronized(_asyncConnection)
    {
        index = [_asyncConnection indexOfObject:(NSURLConnection*)connection];
    }

    [(id)activeInfo[index].delegate performSelectorInBackground:@selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
    [_data[index] release];
    [activeInfo[index].delegate release];
    @synchronized(_asyncConnection)
    {
        [[_asyncConnection objectAtIndex:index] release];
        [_asyncConnection replaceObjectAtIndex:index withObject:nullObject];            
    }
    @synchronized(downloadQueue)
    {
        [downloadQueue removeObject:activeInfo[index]];
        [self NextDownload];
    }
}

- (void)NextDownload
{
    NSLog(@"files remaining: %i", downloadQueue.count);
    if(downloadQueue.count > 0)
    {
        if([self TryDownload:[downloadQueue objectAtIndex:0]])
        {
            [downloadQueue removeObjectAtIndex:0];
        }
    }
}

_asyncConnection是我的下载插槽数组(NSURLConnections) downloadQueue是要下载的网址列表

发生的事情是,一开始一切正常,但在几次下载后,我的连接开始消失。下载开始但连接:didReceiveResponse:永远不会被调用。输出控制台中有一件事我不明白我可能会有所帮助。 Normaly有类似的东西 2010-01-24 21:44:17.504 appName [3057:207] 在我的NSLog消息之前。我想方括号中的数字是某种app:thread id?一切正常,但有一段时间后,“NSLog(@”下载%@在索引%i“,info.url,索引);”消息开始具有不同的第二个数字。当发生这种情况时,我会停止接收该urlconnection的任何回调。

这让我疯了,因为我有严格的截止日期,我找不到问题。我对iphone开发和多线程应用程序没有太多经验。我一直在尝试不同的方法,所以我的代码有点混乱,但我希望你会看到我在这里尝试做的事情:))

顺便说一下,你们中任何一个人都知道我可以使用的现有类/ lib也会有所帮助。我希望并行下载能力o动态添加新文件下载(所以在开头使用所有网址初始化下载对我没有帮助)

5 个答案:

答案 0 :(得分:2)

你有一堆严重的内存问题,以及此代码中的线程同步问题。

我会问下面的问题:你是在某种背景线程上做的吗?为什么? IIRC NSURLConnection已经在后台线程上进行下载,并在创建NSURLConnection的线程上调用您的委托(例如,理想情况下是您的主线程)。

建议您退后一步,重新阅读NSURLConnection文档,然后删除您的背景线程代码以及您不必要地注入的所有复杂性。

进一步的建议:不要试图在两个数组中保持并行定位(以及上面与之相关的一些粗略代码),而是创建一个数组,并且有一个包含NSURLConnection和表示结果的对象的对象。然后,您可以在连接完成后释放连接实例var。完成数据后的父对象(以及数据)。

答案 1 :(得分:1)

我建议你看看这个: http://allseeing-i.com/ASIHTTPRequest/

这是一套非常复杂的课程,包括自由许可条款(免费)。

它可能提供您想要的许多功能。

答案 2 :(得分:0)

此代码段可能是错误的来源,您在对该对象发出异步方法调用后立即释放activeInfo[index].delegate指针指向的对象。

[(id)activeInfo[index].delegate performSelectorInBackground:@selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];

答案 3 :(得分:0)

您使用connection:didFailWithError:吗?可能存在超时阻止成功下载完成。

尝试摆脱@synchronized块,看看会发生什么。

方框中的字符串似乎是您猜到的线程标识符。也许你被锁定在@synchronized。实际上,我没有看到切换线程的原因 - 所有有问题的代码都应该在主线程(performSelectorOnMainThread)中运行...

无论如何,无需同时使用@synchronizedperformSelectorOnMainThread

顺便说一句,我没有看到NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];行。你在哪里发起连接?

至于并行下载 - 我认为你可以使用你在这里使用的相同代码一次下载多个文件。只需为每次下载创建一个单独的连接。

答案 4 :(得分:0)

考虑保留下载队列以及活动连接数,在下载完成且插槽空闲时从队列顶部弹出项目。然后,您可以异步触发NSURLConnection对象并处理主线程上的事件。

如果您发现并行方法禁止在主线程上执行所有处理,请考虑在主线程下载代码和NSURLConnection之间使用中间管理器对象。使用该方法,您将实例化您的管理器并使其在后台线程上同步使用NSURLConnection。然后该管理器完全处理下载并使用performSelectorOnMainThread:withObject:call将结果传递回其主线程委托。每次下载都只是在你有一个空闲的插槽并设置它时创建一个新的管理器对象的情况。