gpt4 book ai didi

objective-c - 如何等待下载大文件?

转载 作者:可可西里 更新时间:2023-11-01 05:31:31 25 4
gpt4 key购买 nike

我有一个应用成功地使用同步方法下载文件(NSData 的 initWithContentsOfURL 和 NSURLConnection 的 sendSynchronousRequest),但现在我需要支持大文件。这意味着我需要一点一点地流式传输到磁盘。尽管流式传输到磁盘并变为异步应该是完全正交的概念,但 Apple 的 API 迫使我为了流式传输而采用异步。

明确地说,我的任务是允许更大的文件下载,而不是重新构建整个应用程序以使其更加异步友好。我没有资源。但我承认依赖于重新架构的方法是有效且好的。

所以,如果我这样做:

NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];

.. 我最终调用了 didReceiveResponse 和 didReceiveData。出色的。但是,如果我尝试这样做:

NSURLConnection* connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];

... didReceiveResponse 和 didReceiveData 永远不会被调用。我已经知道为什么了。奇怪的是,异步下载发生在我正在使用的同一个主线程中。所以当我休眠主线程时,我也在休眠做工作的东西。无论如何,我已经尝试了几种不同的方法来实现我在这里想要的,包括告诉 NSURLConnection 使用不同的 NSOperationQueue,甚至执行 dispatch_async 来创建连接并手动启动它(我不明白这是怎么回事 -我一定没有做对),但似乎没有任何效果。 编辑:我没有做对的是了解运行循环的工作原理,以及您需要在辅助线程中手动运行它们。

等待文件下载完成的最佳方式是什么?

编辑 3,工作代码:以下代码确实有效,但如果有更好的方法,请告诉我。

在建立连接并等待下载完成的原始线程中执行的代码:

dispatch_queue_t downloadQueue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 );
dispatch_async(downloadQueue, ^{
self.connection = [ [ NSURLConnection alloc ] initWithRequest: request delegate: self startImmediately: YES ];
[ [ NSRunLoop currentRunLoop ] run ];
});

while( !self.downloadComplete )
[ NSThread sleepForTimeInterval: .25 ];

在响应连接事件的新线程中执行的代码:

-(void)connection:(NSURLConnection*) connection didReceiveData:(NSData *)data {
NSUInteger remainingBytes = [ data length ];
while( remainingBytes > 0 ) {
NSUInteger bytesWritten = [ self.fileWritingStream write: [ data bytes ] maxLength: remainingBytes ];
if( bytesWritten == -1 /*error*/ ) {
self.downloadComplete = YES;
self.successful = NO;
NSLog( @"Stream error: %@", self.fileWritingStream.streamError );
[ connection cancel ];
return;
}
remainingBytes -= bytesWritten;
}
}

-(void)connection:(NSURLConnection*) connection didFailWithError:(NSError *)error {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = NO;
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.downloadComplete = YES;
[ self.fileWritingStream close ];
self.successful = YES;
}

最佳答案

... didReceiveResponse and didReceiveData are never called. And I've figured out why. Weirdly, the asynchronous download happens in the same main thread that I'm using. It doesn't create a new thread. So when I sleep the main thread, I'm also sleeping the thing doing the work.

没错。连接由运行循环驱动;如果您使线程休眠,运行循环将停止,这会阻止您的连接执行其操作。

所以不要做任何特别的事情。让应用程序坐在那里,运行循环。也许在屏幕上放一个小旋转器来娱乐用户。如果可以的话,去做你的生意吧。如果可能的话,让用户继续使用该应用程序。当连接完成时,您的委托(delegate)方法将被调用,然后您可以对数据执行您需要执行的操作。

当您将代码移动到后台线程时,您将再次需要一个运行循环来驱动连接。因此,您将开始创建一个运行循环,安排您的连接,然后返回。运行循环将继续运行,连接完成时将再次调用您的委托(delegate)方法。如果线程完成,则可以停止运行循环并让线程退出。仅此而已。

示例:让我们具体说明一下。假设您要建立多个连接,一次一个。将 URL 粘贴在可变数组中。创建一个名为(例如)startNextConnection 的方法来执行以下操作:

  • 从数组中获取一个 URL(在此过程中将其删除)

  • 创建 URL 请求

  • 启动一个 NSURLConnection

  • 返回

此外,实现必要的 NSURLConnectionDelegate 方法,特别是 connectionDidFinishLoading:。让该方法执行以下操作:

  • 将数据存储在某处(将其写入文件,将其交给另一个线程进行解析,等等)

  • 调用startNextConnection

  • 返回

如果错误从未发生,那么检索列表中所有 URL 的数据就足够了。 (当然,您会希望 startNextConnection 足够聪明,当列表为空时才返回。)但是错误确实会发生,所以您必须考虑如何处理它们。如果连接失败,是否要停止整个过程?如果是这样,只需让您的 connection:didFailWithError: 方法做一些适当的事情,但不要让它调用 startNextConnection。如果出现错误,是否要跳至列表中的下一个 URL?然后让 ...didFailWithError: 调用 startNextRequest

备选方案:如果您真的想保持同步代码的顺序结构,那么您会得到如下内容:

[self downloadURLs];
[self waitForDownloadsToFinish];
[self processData];
...

然后你必须在不同的线程中进行下载,这样你就可以自由地阻塞当前线程。如果那是您想要的,则使用运行循环设置下载线程。接下来,使用 -initWithRequest:delegate:startImmediately: 创建连接,就像您一直在做的那样,但在最后一个参数中传递 NO。使用 -scheduleInRunLoop:forMode: 将连接添加到下载线程的运行循环,然后使用 -start 方法启动连接。这使您可以自由休眠当前线程。让连接委托(delegate)的完成例程设置一个标志,例如示例中的 self.downloadComplete 标志。

关于objective-c - 如何等待下载大文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10608340/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com