gpt4 book ai didi

objective-c - NSURLConnection 与 NSRunLoopCommonModes

转载 作者:可可西里 更新时间:2023-11-01 03:36:50 25 4
gpt4 key购买 nike

我为我的 iOS 应用程序编写了自己的 HTTPClient 实现,以异步下载指定 URL 的内容。 HTTPClient 使用 NSOperationQueue 来排队 NSURLConnection 请求。我选择 NSOperationQueue 是因为我想在任何时候取消任何或所有正在进行的 NSURLConnection。

我对如何实现我的 HTTPClient 进行了大量研究,我有两种执行 NSURLConnection 的选择:

1) 在单独的辅助线程上执行每个排队的 NSURLConnection。 NSOperationQueue 在后台的辅助线程上执行每个排队的操作,因此我不需要明确地做任何事情来生成辅助线程,除了在 NSOperation 子类的重写 start 方法中启动我的 NSURLConnection 并为生成的辅助线程运行运行循环,直到 connectionDidFinishLoading 或connectionDidFailWithError 被调用。如下所示:

if (self.connection != nil) {
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
} while (!self.isFinished);
}

2) 在主线程上执行每个入队的 NSURLConnection。对于 start 方法中的这个,我使用 performSelectorOnMainThread 并在主线程上再次调用 start 方法。通过这种方法,我使用 NSRunLoopCommonModes 安排了 NSURLConnection,如下所示:

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

我选择了第二种方法并实现了它。根据我的研究,第二种方法似乎更好,因为它不会为每个 NSURLConnection 启动单独的辅助线程。现在在任何时候,在应用程序中可能有许多请求同时进行,并且使用第一种方法,这意味着将产生相同数量的辅助线程,并且在关联的 url 请求完成之前不会返回池。

我的印象是,通过使用 NSRunLoopCommonModes 调度 NSURLConnection,我仍然与第二种方法同时运行。换句话说,对于这种方法,我认为我正在使用 NSRunLoopCommonModes 而不是多线程进行并发,以便 NSURLConnection 的观察者将尽快调用 connectionDidFinishLaunching 或 connectionDidFailWithError,而不管主线程在那个时候对 UI 做了什么时间。

不幸的是,今天早上我的一位同事向我展示了在当前的实现中,NSURLConnection 直到其中一个 View Controller 上的 ScrollView 停止滚动时才会返回,这证明我的所有理解都是错误的。获取数据的 NSURLRequest 在 ScrollView 即将停止滚动时启动,但即使它在 ScrollView 停止调用之前完成,不知何故 NSURLConnection 也不会回调 connectionDidFinishLoading 或 connectionDidFailWithError 直到 ScrollView 完全停止滚动。这意味着在主线程上使用 NSRunLoopCommonModes 调度 NSURLConnection 以获得与 UI 操作(触摸/滚动)的真正并发的整个想法被证明是错误的并且 NSURLConnection 仍然等待直到主线程忙于滚动 ScrollView 。

我尝试切换到使用辅助线程的第一种方法,它非常有效。当 ScrollView 仍在滚动时,NSURLConnection 仍会调用其协议(protocol)方法之一。这很清楚,因为现在 NSURLConnection 没有在主线程上运行,所以它不会等待 ScrollView 停止滚动。

我真的不想使用第一种方法,因为它的多线程成本很高。

如果我对第二种方法的理解不正确,有人可以告诉我吗?如果它是正确的,那么使用 NSRunLoopCommonModes 调度 NSURLConnection 的原因可能是什么没有按预期工作?

如果答案更具描述性,我将不胜感激,因为它应该为我消除更多关于 NSRunLoop 和 NSRunLoopModes 究竟如何工作的疑虑。只是为了说明我已经多次阅读了相关文档。

最佳答案

原来这个问题比我想象的要简单。

我在 NSOperation 子类的 start 方法中有这个

self.connection = [[NSURLConnection alloc] initWithRequest:self.urlRequest
delegate:self];

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

现在的问题是上面的 initWithRequest:delegate: 方法实际上使用 NSDefaultRunLoopMode 在默认运行循环中调度 NSURLConnection,并完全忽略了我实际尝试使用 NSRunLoopCommonModes 调度它的下一行。通过将上面两行更改为下面的行,可以按预期工作。

self.connection = [[NSURLConnection alloc] initWithRequest:self.urlRequest
delegate:self startImmediately:NO];

[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

[self.connection start];

这里的实际问题是我必须使用带有参数 startImmediately 的构造方法来初始化 NSURLConnection。当我为参数 startImmediately 传递 NO 时,不会使用默认运行循环安排连接。它可以通过调用 scheduleInRunLoop:forMode: 方法在运行循环和选择的模式中进行调度。

现在,从方法 scrollViewWillEndDragging:withVelocity:targetContentOffset 发起的 NSURLConnection 正在调用其委托(delegate)方法 connectionDidFinishLoading/connectionDidFailWithError,而 ScrollView 仍在滚动且尚未完成滚动。

我希望这对某人有所帮助。

关于objective-c - NSURLConnection 与 NSRunLoopCommonModes,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10132948/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com