我有一个成功使用同步方法下载文件的应用程序(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;
}