【问题标题】:iOS - Async NSURLConnection inside NSOperationiOS - NSOperation 中的异步 NSURLConnection
【发布时间】:2013-02-25 12:03:04
【问题描述】:

我知道这个问题在 SO 上被问过很多次,但我没有设法让它在我的项目中发挥作用......

所以,我想继承NSOperation 并让它使用NSURLConnection 下载文件。正确的方法是什么? 这是我的代码不起作用: 首先,我将所有操作添加到一个循环中:

DownloadFileOperation *operation;
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
for (int i=0; i<10; i++) {
operation = [[DownloadFileOperation alloc] init];
operation.urlString = pdfUrlString;
[queue addOperation:operation];
operation = nil; }

这是我的子类:

@interface DownloadHandbookOperation : NSOperation <NSURLConnectionDelegate>
{

}

@property (strong, nonatomic) NSString *urlString;

@end


@implementation DownloadHandbookOperation
{
    NSString *filePath;
    NSFileHandle *file;
    NSURLConnection * connection;
}

- (void)start
{
    if (![NSThread isMainThread])
    {
        [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
        return;
    }

    NSURL *url = [[NSURL alloc] initWithString:[self.urlString stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding]];

    NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
    [req addValue:@"Basic ***=" forHTTPHeaderField:@"Authorization"];
    connection = [[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES];

}

- (void)connection:(NSURLConnection *)conn didReceiveResponse:(NSURLResponse *)response
{
    NSString *filename = [[conn.originalRequest.URL absoluteString] lastPathComponent];
    filename = [filename stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:filename];
    [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];

    file = [NSFileHandle fileHandleForUpdatingAtPath:filePath] ;
    if (file)
    {
        [file seekToEndOfFile];
    }
    else
        [self finish];
}

- (void)connection:(NSURLConnection *)conn didReceiveData:(NSData *)data
{
    if (file) {
        [file seekToEndOfFile];
    }
    [file writeData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)conn
{
    [file closeFile];
    [self finish];
}

- (void)connection:(NSURLConnection *)conn didFailWithError:(NSError *)error
{
    connection = nil;

    [self finish];
}

- (void)cancel
{
    [super cancel];
    [connection cancel];
}


- (void)finish
{
    NSLog(@"operationfinished.");
}


@end

我做错了什么?

【问题讨论】:

  • 我认为 NSURLConnection 必须从主线程调用,如果从后台线程调用它不会做任何事情......而且即使它是从主线程调用它实际上仍然可以后台的网络东西,然后再次从主线程调用你的委托函数......
  • 对,那我怎样才能让我的 NSOperation 在主线程上执行呢?
  • 试试[[NSOperationQueue mainQueue] addOperation:operation]...
  • 它有效,但现在我无法控制我的队列和取消/跟踪操作?
  • 是的。 NSURLConnection 在后台线程上运行,另一种描述方式是说 NSURLConnection “是并发的”。你如何停止 NSOperation 运行“后台”?您通过返回isConcurrent = YES 告诉它您的操作已经并发。这告诉 NSOperation 不要为此操作创建单独的后台线程。您的操作将处理此问题。一旦你掌握了控制权,你还必须通过isFinishedisExecuting 告诉你什么时候完成。我会尝试找出一个很好的例子并将其添加到我的答案中。

标签: ios xcode cocoa-touch nsurlconnection nsoperation


【解决方案1】:

您需要正确配置您的操作以作为“并发操作”执行

Concurrency Programming Guide: Configuring Operations for Concurrent Execution

您需要返回isConcurrent = YES 并以符合KVO 的方式正确管理其他状态标志isExecutingisFinished


为了说明这里的总体思路,Pulse 的工程师发表了一篇文章,其中描述了他们的解决方案,其中包含一些易于理解的演示代码,您可以下载和查看。

Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues**

此代码还通过确保它在主线程上启动它来处理在具有活动运行循环的线程上启动 NSURLConnection 的要求。

(** 链接现在指向archive.org,我认为Pulse 已被收购并已关闭他们的旧网站)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-02
  • 2012-09-19
  • 1970-01-01
  • 2010-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多