使用NSURLConnection同步下载和进度回调?

时间:2014-08-06 06:31:45

标签: ios objective-c xcode nsurlconnection nsurlconnectiondelegate

我尝试使用NSURLConnection通过进程回调实现同步下载。 当调用[连接开始]时,没有任何反应 - 委托回调方法不仅仅被调用(我在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

2 个答案:

答案 0 :(得分:0)

您的等待循环将CPU主线程锁定为100%,使用以下命令修补等待循环:

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

答案 1 :(得分:0)

感谢 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]];
}