2010-01-24 33 views
5

我想创建一个类,将同时处理多个下载(我需要下载很多小文件),我有“消失”连接的问题。iPhone上的并发背景下载

我有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是URL列表,下载

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

由于我有严格的最后期限,我无法找到问题,这一直让我疯狂。我没有很多iphone开发和多线程应用程序的经验。我一直在尝试不同的方法,所以我的代码有点杂乱,但我希望你会看到我在这里试图做的:)

btw是你们中的任何人都知道现有的类/ lib我可以使用,这将是有益的以及。我想要平行下载能力o动态添加新的文件下载(所以初始化下载器在所有的URL对我没有帮助)

回答

2

你有一堆严重的内存问题,并在这段代码中的线程同步问题。

与其进入他们所有的问题,我会问以下问题:您是在某种背景线程上执行此操作的?为什么? IIRC NSURLConnection已经在后台线程上下载并在创建NSURLConnection的线程上调用委托(例如理想的主线程)。

建议您退后一步,重新阅读NSURLConnection文档,然后删除后台线程代码以及您不必要地注入的所有复杂性。进一步的建议:不要试图在两个数组中保持平行定位(以及上面涉及的一些粗略的代码),而是创建一个数组并且包含一个包含NSURLConnection和表示结果的对象的对象。然后,您可以在连接完成时释放连接实例var。当你完成数据的时候,父对象(以及数据)。

+0

感谢您的提示。我完全重写了我的课,并按预期工作。对不起,但我完全忘记了这是很久以前解决的:) – Lope 2010-10-21 21:46:07

0

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

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

这个下载器最初是顺序的,它工作得很好,这部分代码来自以前的功能部分。我认为performSelector保留了我作为参数发送的对象。无论如何,即使我将它注释掉,它也不会改变任何东西 – Lope 2010-01-24 22:30:58

0

你使用connection:didFailWithError:?可能会有一个超时,阻止成功下载完成。

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

方括号内的字符串似乎是您猜对的线程标识符。所以也许你会被锁在@synchronized。其实,我看不出切换线程有原因的 - 所有有问题的代码应该在主线程(performSelectorOnMainThread)运行...

总之,没有必要同时使用@synchronizedperformSelectorOnMainThread

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

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

+0

连接在TryDownload函数中启动 ([_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]]; ) 我使用didFailWithError,但它是相似的下载成功,它不会被调用,当我失去了一个连接,所以我删除它(我应该提到,对不起)。 当我删除@synchronized块时,即使使用performSelectorOnMainThread,我也得到“枚举时更改”错误。我正在尝试这两种方法,但他们都没有工作 – Lope 2010-01-26 07:57:53

+0

尝试使用NSNotificationCenter启动新的下载,而不是performSelectorOnMainThread ... – 2010-01-26 22:17:53

0

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

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