异步 NSURLConnection 中断计时器
Asynchronous NSURLConnection breaks timers
我设置了一个 class 来处理我的网络 API 呼叫。这是使用 NSMutableURLRequest
和 NSRLlConnection
完成的。我最初使用 connectionWithRequest: delegate:
并且在大多数情况下效果很好,除非我依赖这个请求是真正异步的,而不仅仅是在主 运行 循环中部分执行。
为此,我想我会使用非常方便的 sendAsynchronousRequest: queue: completionHandler:
并且起初在我所有的单元测试中我认为它工作得很好。它异步执行,我的信号量被等待并正确发出信号,太棒了。
直到我尝试在我的实际应用程序中重新使用我的 Web 服务的这个新修改版本 class。我的应用程序的一部分播放视频并使用重复 NSTimer
根据视频的当前播放时间更新部分屏幕。由于某些未知原因,只要我至少执行了这些新的异步 NSURLConnection
之一,视频播放和计时器就不再工作。
这是我初始化连接的方式:
[NSURLConnection sendAsynchronousRequest:requestMessage
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if ( data.length > 0 && connectionError == nil )
{
_webServiceData = [data mutableCopy];
[self performSelector:@selector(connectionDidFinishLoading:) withObject:nil];
}
else if ( connectionError != nil )
{
_webServiceData = [data mutableCopy];
[self performSelector:@selector(webServiceDidFinishExecutingWithError:) withObject:connectionError];
}
}];
以下是我如何初始化重复计时器:
playbackTimeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkPlaybackTime) userInfo:nil repeats:YES];
而且我完全不知道为什么异步 NSURLConnection
会导致我的应用程序中与停止运行完全无关的方面。
编辑:
为了澄清,我有 ViewControllerA
执行网络请求以检索一些数据。成功检索到该数据后,ViewControllerA
会自动转到 ViewControllerB
。 ViewControllerB
的 viewWillAppear
是我设置电影播放器和计时器的地方。
以后不要再用信号量让测试等待了。使用专为测试异步进程而设计的新 XCTestExpectation
。
并且与传统的信号量技巧不同,使用测试期望不会阻塞主线程,因此如果您有需要主线程的完成块或委托,您可以结合测试期望来执行此操作。
您显然正在做一些不起作用的事情,因为您在后台队列中 运行ning 了它。典型问题包括
如果您试图从后台线程 运行 该计时器,那是行不通的,除非您使用标准计时器解决方法之一(将其安排在主 运行循环,将定时器的创建分派回主线程,使用分派定时器等)。
之前已经非常详细地描述了这些备选方案,结果证明您是从 viewWillAppear
启动计时器,所以这都是学术性的。
任何 UI 更新(包括执行转场、重新加载表等)。
同步从后台线程更新的 class 属性时应小心(这些也可能最好分派到主线程)。
无论如何,您可以通过告诉 sendAsynchronousRequest
到 运行 它在主队列上的完成块来解决这个问题:
[NSURLConnection sendAsynchronousRequest:requestMessage
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
...
}];
然后,完成块将在后台队列中有 运行,一切可能都会好起来。或者您可以手动将完成处理程序的调用分派回主队列。
但要确保任何 UI 更新(包括以编程方式执行 segue)在主线程上 运行(通过 运行 在主线程上对整个完成块进行线程,或手动将相关调用分派到主线程)。
我设置了一个 class 来处理我的网络 API 呼叫。这是使用 NSMutableURLRequest
和 NSRLlConnection
完成的。我最初使用 connectionWithRequest: delegate:
并且在大多数情况下效果很好,除非我依赖这个请求是真正异步的,而不仅仅是在主 运行 循环中部分执行。
为此,我想我会使用非常方便的 sendAsynchronousRequest: queue: completionHandler:
并且起初在我所有的单元测试中我认为它工作得很好。它异步执行,我的信号量被等待并正确发出信号,太棒了。
直到我尝试在我的实际应用程序中重新使用我的 Web 服务的这个新修改版本 class。我的应用程序的一部分播放视频并使用重复 NSTimer
根据视频的当前播放时间更新部分屏幕。由于某些未知原因,只要我至少执行了这些新的异步 NSURLConnection
之一,视频播放和计时器就不再工作。
这是我初始化连接的方式:
[NSURLConnection sendAsynchronousRequest:requestMessage
queue:[[NSOperationQueue alloc] init]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
if ( data.length > 0 && connectionError == nil )
{
_webServiceData = [data mutableCopy];
[self performSelector:@selector(connectionDidFinishLoading:) withObject:nil];
}
else if ( connectionError != nil )
{
_webServiceData = [data mutableCopy];
[self performSelector:@selector(webServiceDidFinishExecutingWithError:) withObject:connectionError];
}
}];
以下是我如何初始化重复计时器:
playbackTimeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(checkPlaybackTime) userInfo:nil repeats:YES];
而且我完全不知道为什么异步 NSURLConnection
会导致我的应用程序中与停止运行完全无关的方面。
编辑:
为了澄清,我有 ViewControllerA
执行网络请求以检索一些数据。成功检索到该数据后,ViewControllerA
会自动转到 ViewControllerB
。 ViewControllerB
的 viewWillAppear
是我设置电影播放器和计时器的地方。
以后不要再用信号量让测试等待了。使用专为测试异步进程而设计的新
XCTestExpectation
。并且与传统的信号量技巧不同,使用测试期望不会阻塞主线程,因此如果您有需要主线程的完成块或委托,您可以结合测试期望来执行此操作。
您显然正在做一些不起作用的事情,因为您在后台队列中 运行ning 了它。典型问题包括
如果您试图从后台线程 运行 该计时器,那是行不通的,除非您使用标准计时器解决方法之一(将其安排在主 运行循环,将定时器的创建分派回主线程,使用分派定时器等)。
之前已经非常详细地描述了这些备选方案,结果证明您是从
viewWillAppear
启动计时器,所以这都是学术性的。任何 UI 更新(包括执行转场、重新加载表等)。
同步从后台线程更新的 class 属性时应小心(这些也可能最好分派到主线程)。
无论如何,您可以通过告诉
sendAsynchronousRequest
到 运行 它在主队列上的完成块来解决这个问题:[NSURLConnection sendAsynchronousRequest:requestMessage queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) ... }];
然后,完成块将在后台队列中有 运行,一切可能都会好起来。或者您可以手动将完成处理程序的调用分派回主队列。
但要确保任何 UI 更新(包括以编程方式执行 segue)在主线程上 运行(通过 运行 在主线程上对整个完成块进行线程,或手动将相关调用分派到主线程)。