[英]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.