同步下载与NSURLConnection和进度回调



我试图实现与NSURLConnection的进度回调同步下载。当[connection start]被调用时,什么都没有发生-委托回调方法不仅仅被调用(我在XCTestCase中测试OSX)。怎么了?

// header
@interface ASDownloadHelper : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
{
    NSMutableData *_receivedData;
    NSUInteger _expectedBytes;
    id<ASDownloadHelperListener> _listener;
    NSError *_error;
    BOOL _finished;
    id _finishedSyncObject;
}
- (void) download: (NSString*)url file:(NSString*)file listener:(id<ASDownloadHelperListener>)listener;
@end
// impl
@implementation ASDownloadHelper
// delegate
- (void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [_receivedData setLength:0];
    _expectedBytes = [response expectedContentLength];
}
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_receivedData appendData:data];
    int percent = round( _receivedData.length * 100.0 / _expectedBytes );
    [_listener onDownloadProgress:_receivedData.length total:_expectedBytes percent:percent];
}
- (void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    _error = error;
    [self setFinished:YES];
}
- (NSCachedURLResponse *) connection:(NSURLConnection*)connection
                   willCacheResponse:(NSCachedURLResponse*)cachedResponse {
    return nil;
}
- (void) connectionDidFinishLoading:(NSURLConnection *)connection {
    [self setFinished: YES];
}
- (BOOL) isFinished {
    @synchronized(_finishedSyncObject) {
        return _finished;
    }
}
- (void) setFinished: (BOOL)finished {
    @synchronized(_finishedSyncObject) {
        _finished = finished;
    }
}
// ---
- (void) download: (NSString*)downloadUrl file:(NSString*)file listener:(id<ASDownloadHelperListener>)listener {
    _listener = listener;
    _finished = NO;
    _finishedSyncObject = [[NSObject alloc] init];
    _error = nil;
    NSURL *url = [NSURL URLWithString:downloadUrl];
    NSURLRequest *request = [NSURLRequest requestWithURL:url
                                                cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                            timeoutInterval:30];
    _receivedData = [[NSMutableData alloc] initWithLength:0];
    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request
                                                                  delegate:self];
    [connection start];
    // block the thread until downloading finished
    while (![self isFinished]) { };
    // error?
    if (_error != nil) {
        @throw _error;
        return;
    }
    // success
    [_receivedData writeToFile:file atomically:YES];
    _receivedData = nil;
}
@end

你的等待循环锁定CPU主线程为100%,修补你的等待循环:

...
// block the thread until downloading finished
while (![self isFinished]) 
{ 
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
};
...

多亏了quellish我发现调用队列不应该被阻塞作为回调调用(委托方法)是在调用者线程上下文中完成的。在我的情况下,我是在主测试线程中运行它,所以我不得不做一些解决方案(并在主线程中睡眠几秒钟让下载完成):

- (void)testDownload
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        // ...
        [_downloadHelper download:repositoryUrl file:downloadedFile listener:downloadlistener];
        // progress callbacks are invoked in this thread context, so it can't be blocked
        // ...
        XCTAssertNotNil( ... );
    });
    // block main test queue until downloading is finished
    [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}

最新更新