繁体   English   中英

在NSThread或NSOperation中使用NSUrlConnection

[英]Using NSUrlConnection inside either NSThread or NSOperation

我正在开发一个静态库,该库需要在后台执行一些操作,而无需与主线程进行交互。 为了给您一个想法,请考虑仅记录一些用户事件。 库必须继续执行此操作,直到用户退出应用程序或将其发送到后台(按“主页”按钮)为止-换句话说,它需要继续在循环内执行操作。

主应用程序线程与生成的线程之间的唯一交互是,主应用程序线程有时会将一些东西(事件对象)放入队列中,生成的线程可以读取/使用该队列。 除此之外,生成的线程会一直继续下去,直到应用程序存在或出现背景为止。

产生的线程需要做的一部分工作(尽管不是全部)涉及向HTTP服务器发送数据。 我本来以为可以很容易地将NSThread子类化,覆盖其主要方法,并且只需对连接进行某种超时的NSUrlConnection同步调用,这样线程就不会永远挂起。 例如,在Java / Android中,我们只是Thread的子类,重写start()方法并调用同步的HTTP GET方法(例如,来自Apache的HttpClient类)。 这非常容易并且可以正常工作。 但是从我在这里和其他地方所看到的,显然在iOS上,这比这要复杂得多,对于什么才是真正有效的最佳方法,我有些困惑。

那么我应该子类化NSThread并以某种方式使用NSUrlConnection吗? 似乎异步NSUrlConnection在NSThread中不起作用,因为未调用委托方法,但是同步方法又如何呢? 我是否需要使用和配置RunLoop并设置自动释放池? 还是应该使用NSOperation? 在我看来,我试图做的事很普遍-有人能有效地做到这一点吗?

据我了解,要异步使用NSURLConnection您需要一个运行循环。 即使您使用NSOperation您仍然需要一个运行循环。

我看到的所有示例都使用Main Thread来启动具有运行循环的NSURLConnection 设置了使用NSOperation的示例,因此操作是Concurrent ,它告诉NSOperationQueue不提供它自己的线程,然后确保在主线程上启动NSURLConnection ,例如通过对performSelectorOnMainThread:的调用performSelectorOnMainThread:

这是一个例子:

Pulse Engineering博客:使用NSOperationQueues的并发下载

您也可以在LinkedImageFetcher示例中的Apple文档中搜索QRunLoopOperation ,该示例是一个示例类,它显示了这种情况的来龙去脉。

(尽管我不确定我实际上是否看到过该示例显示如何运行自己的runloop的任何代码,但此示例仍然依赖于主线程。)

我已经使用中央集中调度(GCD)方法来实现此目的。 这是一个在简单测试应用程序中为我工作的示例(我不确定它是否适用于静态库,但可能值得一看)。 我正在使用ARC。

在示例中,我从viewDidLoad方法开始一些后台工作,但是您可以从任何地方开始。 关键是“ dispatch_async(dispatch_get_global_queue…”)在后台线程中运行该块。有关该方法的详细说明,请参见此答案: https : //stackoverflow.com/a/12693409/215821

这是我的viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), 
        ^(void) {
            [self doStuffInBackground];
        });
}

此时,doStuffInBackground方法正在后台运行,因此您可以同步使用NSURLConnection。 在我的示例中,该方法循环进行网络调用,直到大概有其他代码设置backgroundStuffShouldRun = false。 进行10秒超时的网络通话。 通话结束后,我将更新UI标签只是为了显示进度。 注意,UI更新是使用“ dispatch_async(dispatch_get_main_queue()…”执行的。),这将根据需要在UI线程上运行UI更新。

此后台工作的一个潜在问题是:无法取消http请求本身。 但是,如果超时时间为10秒,那么在局外人(可能是您的UI中的某个事件)设置backgroundStuffShouldRun = false之后,您最多要等待10秒,线程才能中止自身。

- (void)doStuffInBackground
{
    while (backgroundStuffShouldRun) {
        // prepare for network call...
        NSURL* url = [[NSURL alloc] initWithString:@"http://maps.google.com/maps/geo"];

        // set a 10 second timeout on the request
        NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10];

        NSError* error = nil;
        NSURLResponse *response = nil;

        // make the request
        NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

        // were we asked to stop the background processing?
        if (!backgroundStuffShouldRun) {
            return;
        }

        // process response...

        NSString* status = @"Success";

        if (error) {
            if (error.code == NSURLErrorTimedOut) {
                // handle timeout...
                status = @"Timed out";
            }
            else {
                // handle other errors...
                status = @"Other error";
            }
        }
        else {
            // success, handle the response body
            NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            NSLog(@"%@", dataAsString);
        }


        // update the UI with our status
        dispatch_async(dispatch_get_main_queue(), ^{
            [statusLabel setText:[NSString stringWithFormat:@"completed network call %d, status = %@", callCount, status]];
        });

        callCount++;
        sleep(1); // 1 second breather. not necessary, but good idea for testing
    }

}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM