异步网络 +线程问题

发布于 2024-09-04 11:35:02 字数 1871 浏览 5 评论 0 原文

我启动一个网络请求,假设不需要登录凭据即可与目标服务器通信。如果需要,我会收到身份验证质询,此时我会显示一个视图,请求用户提供所述凭据。当提供它们时,我使用这些凭据重新启动网络请求。

只要我一次只执行一个请求,这一切都很好。但我通常不是。

当两个请求启动时,我收到第一个挑战,并显示提示(使用 -presentModalViewController:)。然后第二个挑战出现了。当它尝试显示第二个提示时我崩溃了。

我将其中的大部分内容封装在 @synchronized() 块中,但这没有任何效果,因为这些委托方法都是在同一(主)线程上调用的。文档说委托方法是在启动连接的同一线程上调用的。好的,没问题;我将只编写一个使用 -performSelectorInBackground: 在后台线程上运行的方法,

NSURLConnection *connection = [[NSURLConnection alloc] 
                               initWithRequest:request 
                                      delegate:self
                              startImmediately:NO];
[connections addObject:connection];
[self performSelectorInBackground:@selector(startConnection:) 
                       withObject:connection];
[connection release];

- (void)startConnection:(NSURLConnection *)connection {
   NSAutoreleasePool *pool = [NSAutoreleasePool new];
   [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] 
                         forMode:NSDefaultRunLoopMode];
   [connection start];
   [pool drain];
}

该方法应该将每个网络请求及其回调放在它自己的线程上,然后我的 @synchronized() 块将生效。

-initWithRequest:... 的文档指出“发送给委托的消息将在调用此方法的线程上发送。默认情况下,为了使连接正常工作,调用线程的运行循环必须在默认运行循环模式下运行。 ”好的,我正在这样做。

他们还指出“如果您传递 NO [for startImmediately],则必须在启动之前在运行循环中安排连接。”好吧,我也在这么做。

此外,NSRunLoop 的文档指出“每个 NSThread 对象,包括应用程序的主线程,都会根据需要自动为其创建一个 NSRunLoop 对象。如果您需要访问当前线程的运行循环,则可以使用类方法 currentRunLoop 来执行此操作。”我假设这适用于通过调用 -performSelectorInBackground... 创建的后台线程(当我在 -startConnection: 方法中执行 'po [NSClassFromString(@"NSRunLoop") currentRunLoop]' 时,情况似乎确实如此)。

-startConnection: 方法确实被调用。但在启动连接后,我现在再也没有收到任何回调。没有 -connectionDid... 委托方法。 (我什至尝试显式启动线程的运行循环,但这没有什么区别;我以前使用过这样的线程,而且以前从未需要手动启动运行循环——但我现在抓住了救命稻草。 ..)

我想我已经想出了一种解决方法,让我一次只处理一个请求,但它很笨拙,我想以正确的方式做到这一点。但是,我在这里缺少什么?

谢谢! 兰迪

I kick off a network request, assuming no login credentials are required to talk to the destination server. If they are required, then I get an authentication challenge, at which point I display a view requesting said credentials from the user. When they are supplied, I restart the network request, using those credentials.

That's all fine and dandy, as long as I only do one request at a time. But I'm not, typically.

When both requests are kicked off, I get the first challenge, and present the prompt (using -presentModalViewController:). Then the 2nd challenge comes in. And I crash when it tries to display the 2nd prompt.

I have the bulk of this wrapped in an @synchronized() block, but this has no effect because these delegate methods are all being called on the same (main) thread. The docs say the delegate methods are called on the same thread in which the connection was started. OK, no problem; I'll just write a method that I run on a background thread using -performSelectorInBackground:

NSURLConnection *connection = [[NSURLConnection alloc] 
                               initWithRequest:request 
                                      delegate:self
                              startImmediately:NO];
[connections addObject:connection];
[self performSelectorInBackground:@selector(startConnection:) 
                       withObject:connection];
[connection release];

- (void)startConnection:(NSURLConnection *)connection {
   NSAutoreleasePool *pool = [NSAutoreleasePool new];
   [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] 
                         forMode:NSDefaultRunLoopMode];
   [connection start];
   [pool drain];
}

which should put every network request, and its callbacks, on its own thread, and then my @synchronized() blocks will take effect.

The docs for -initWithRequest:... state "Messages to the delegate will be sent on the thread that calls this method. By default, for the connection to work correctly the calling thread’s run loop must be operating in the default run loop mode." Ok, I'm doing that.

They also state "If you pass NO [for startImmediately], you must schedule the connection in a run loop before starting it." OK, I'm doing that, too.

Furthermore, the docs for NSRunLoop state "Each NSThread object, including the application’s main thread, has an NSRunLoop object automatically created for it as needed. If you need to access the current thread’s run loop, you do so with the class method currentRunLoop." I'm assuming this applies to the background thread created by the call -performSelectorInBackground... (which does appear to be the case, when I execute 'po [NSClassFromString(@"NSRunLoop") currentRunLoop]' in the -startConnection: method).

The -startConnection: method is indeed being called. But after kicking off the connection, I now never get any callbacks on it. None of the -connectionDid… delegate methods. (I even tried explicitly starting the thread's run loop, but that made no difference; I've used threads like this before, and I've never had to start the run loop manually before--but I'm now grasping at straws...)

I think I've come up with a workaround such that I only handle one request at a time, but it's kludgy and I'd like to do this the Right Way. But, what am I missing here?

Thanks!
randy

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

傲鸠 2024-09-11 11:35:02

是否到达了[pool dance];线?我猜是的。此后 -startConnection 方法退出,此时后台线程完成并销毁。这可能不是您想要的……

您必须运行 runloop。你说你做了这件事,但没有什么区别。这说明你做错了。阅读线程编程指南中的 Runloop 部分(最好去正确的文档而不是听我说)。请注意,像 -performSelectorInBackground 这样的有用方法可能会使多线程看起来很有吸引力,但它很困难。

另外,你的意思是

'包裹在@synchronized()块中,
但这没有效果,因为这些
委托方法都被调用
在同一个(主)线程上'

@synchronized() 将在主线程上执行与在后台线程上相同的操作。您认为它会做什么?

Is the [pool drain]; line reached? I'm guessing it is. After this the -startConnection method exits, at that point the background thread is finished and destroyed. This is probably not what you want…

You have to run the runloop. You say you did this and it made no difference. This means you did it wrong. Read the Runloop section in the Threaded Programming Guide (It is better to go to the proper docs than hear it off me). Beware, useful methods like -performSelectorInBackground might make multithreading seem appealing, but it is difficult.

Also, what do you mean

'wrapped in an @synchronized() block,
but this has no effect because these
delegate methods are all being called
on the same (main) thread'

?

@synchronized() will do the same on the main thread as it does on a background thread.. what do you think it does?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文