NSOperation和NSURLConnection神秘 [英] NSOperation and NSURLConnection mystified

查看:54
本文介绍了NSOperation和NSURLConnection神秘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用NSOperation和NSOperationQueue从某些服务器下载多个图像.我的主要问题是下面的代码段和此链接

I am trying to download multiple images from some server using NSOperation and NSOperationQueue. My main question is what is the difference between the code snippet below, and this link http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/, performance wise? I prefer the second solution, because we have a lot more control over the operations, it is a lot cleaner, and if a connection fails you can handle it properly.

如果我尝试使用以下代码从服务器上下载约300张图片,则我的应用会有很大的延迟,如果我启动该应用并立即进入主屏幕,然后返回该应用,我会崩溃,因为没有足够的时间使该应用程序再次激活.如果我取消注释[queue setMaxConcurrentOperationCount:1],则用户界面会响应,并且在进入背景并返回到前景时不会崩溃.

If I try to download about 300 images from a server with the code below, my app will have a pretty big delay, and if I launch the app, and go to the home screen immediately, then go back into the app, I will crash, because there wasn't wasn't enough time for the app to become active again. If I uncomment [queue setMaxConcurrentOperationCount:1], the user interface is responsive and it won't crash if it enters the background and returns to the foreground.

但是,如果我实现与上面的链接类似的内容,则无需担心设置maxConcurrentOperationCount,则默认值很好.一切都是响应式的,没有崩溃,而且似乎所有队列都更快地完成了.

But if I implement something similar to the link above, I don't need to worry about setting maxConcurrentOperationCount, the default value is fine. Everything is responsive, no crashing, and it seems like all the queue gets completed faster.

这使我想到第二个问题,为什么[queue setMaxConcurrentOperationCount:1]在下面的代码中产生如此大的影响?从文档中,我认为将maxConcurrentOperationCount保留为默认值很好,这只是告诉队列根据某些因素决定最佳值.

So this brings me to my second question, why does [queue setMaxConcurrentOperationCount:1] have such a big affect in my code below? From the documentation, I thought that leaving the maxConcurrentOperationCount at its default value was fine, and that just tells the queue to decide what the best value should be based on certain factors.

这是我关于Stack Overflow的第一篇文章,希望这很有道理,谢谢您的帮助!

This is my first post on Stack Overflow so hopefully this makes sense, thanks for any help!

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//[queue setMaxConcurrentOperationCount:1];

for(NSURL *URL in URLArray) {
    [queue addOperationWithBlock:^{
        NSHTTPURLResponse *response = nil;
        NSError *error = nil;
        NSData * data = [NSURLConnection sendSynchronousRequest:[NSURLRequest requestWithURL:URL] returningResponse:&response error:&error];

        if(!error && data) {
            [data writeToFile:path atomically:YES];
        }
    }];
}

推荐答案

我将以相反的顺序解决这些问题.您问:

I'm going to tackle these in reverse order. You ask:

这使我想到第二个问题,为什么[queue setMaxConcurrentOperationCount:1]在下面的代码中产生如此大的影响?从文档中,我认为将maxConcurrentOperationCount保留为默认值很好,这只是告诉队列根据某些因素决定最佳值.

So this brings me to my second question, why does [queue setMaxConcurrentOperationCount:1] have such a big affect in my code below? From the documentation, I thought that leaving the maxConcurrentOperationCount at its default value was fine, and that just tells the queue to decide what the best value should be based on certain factors.

使用 NSURLConnection ,您不能同时下载四个或五个以上的连接.因此,如果您未设置 maxConcurrentOperationCount ,则操作队列将不知道您正在处理 NSURLConnection ,因此当您添加300 NSOperation 将对象添加到您的队列中,该队列将尝试同时启动大量的对象(我认为是64-ish).但是,由于只能同时运行4个或5个 NSURLConnection 请求,因此其余由队列启动的请求将等待,直到4个或5个可能的连接之一可用,并且下载请求如此之多,他们中的许多人很可能会超时并失败.

With NSURLConnection, you cannot have more than four or five connections downloading concurrently. Thus, if you don't set maxConcurrentOperationCount, the operation queue doesn't know you're dealing with NSURLConnection and therefore when you add 300 NSOperation objects to your queue, the queue will try to start a very large number of them (64-ish, I think) concurrently. But since only 4 or 5 NSURLConnection requests can run concurrently, the rest of them that were started by the queue will wait until one of the four or five possible connections are available, and with so many download requests, it's quite likely that many of them will time out and fail.

使用1的 maxConcurrentOperationCount 可以对这个问题应用比较繁琐的解决方案,一次只能运行一个.我建议一种折衷方法,即 maxConcurrentOperationCount 为4,它具有一定程度的并发性(并获得了巨大的性能提升),但又不至于让我们冒着连接超时和失败的风险.

By using maxConcurrentOperationCount of 1, that applies a rather heavy-handed solution to this problem, only running one at a time. I'd suggest a compromise, namely a maxConcurrentOperationCount of 4, which enjoys a degree of concurrency (and huge performance gain), but not so many that we risk having connections time out and fail.

回到Dave Drubin的 NSOperation ,他对包裹在操作中的 synchronousRequest 有了很大的改进.话虽如此,他却忽略了并发请求的一个相当基本的功能,即取消.您应该进行检查以查看操作是否已取消,如果已取消,请取消连接:

Going back to Dave Drubin's NSOperation, his is great improvement over your synchronousRequest wrapped in an operation. Having said that, he's neglected to address a fairly basic feature of concurrent requests, namely cancelation. You should include a check to see if the operation has been canceled, and if so, cancel the connection:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if ([self isCancelled]) {
        [connection cancel];
        [self finish];
        return;
    }

    [_data appendData:data];
}

同样,当他也应该在 start 方法中执行此操作时.

Likewise, when he should be doing that in the start method, too.

- (void)start
{
    // The Apple docs say "Always check for cancellation before launching the task."

    if ([self isCancelled]) {
        [self willChangeValueForKey:@"isFinished"];
        _isFinished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }

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

    NSLog(@"opeartion for <%@> started.", _url);

    [self willChangeValueForKey:@"isExecuting"];
    _isExecuting = YES;
    [self didChangeValueForKey:@"isExecuting"];

    NSURLRequest * request = [NSURLRequest requestWithURL:_url];
    _connection = [[NSURLConnection alloc] initWithRequest:request
                                                  delegate:self];
    if (_connection == nil)
        [self finish];
}

我可能会建议对Dave的示例进行其他风格上的改进,但这只是次要的东西,我认为他可以从中获得大部分的帮助.无法检查取消是我身上唯一明显的大问题.

I might suggest other stylistic improvements to Dave's example, but it's all minor stuff, and I think he got most of the big picture stuff spot on. The failure to check for cancelation was the only obvious big issue that leapt out at me.

无论如何,有关并发操作的讨论,请参见

Anyway, for discussion of concurrent operations, see the Configuring Operations for Concurrent Execution section of the Concurrency Programming Guide.

此外,在测试如此大的下载量时,我建议您使用Network Link Conditioner(适用于Mac/模拟器,可通过"Xcode"上硬件IO工具"下的下载项)对您的应用程序进行压力测试-打开开发者工具"-更多开发者工具";如果您启用iOS设备进行开发,则设置"应用中的常规"-开发者"下还会有一个网络链接调节器设置).当我们在高度优化的开发环境中测试我们的应用程序时,许多与超时相关的问题都不会显现出来.使用网络链接调节器来模拟不理想的真实场景非常重要.

Also, when testing huge downloads like these, I'd encourage you to stress test your app with the Network Link Conditioner (available for the Mac/simulator as a download available under "Hardware IO tools" on the "Xcode" - "Open Developer Tool" - "More Developer Tools"; if you enable your iOS device for development, there is also a network link conditioner setting under "General" - "Developer" in the Settings app). A lot of these timeout-related problems don't manifest themselves when we test our apps in our highly optimized scenario of our development environment. It's important to use the network link conditioner to simulate less than ideal, real-world scenarios.

这篇关于NSOperation和NSURLConnection神秘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆