gpt4 book ai didi

ios - 自从升级到 iOS10 后,带有 NSRunLoop 的信号量无法正常工作

转载 作者:行者123 更新时间:2023-12-01 18:43:17 25 4
gpt4 key购买 nike

我在我的应用程序中使用官方 Ricoh Theta iOS SDK (Link) 连接到 360° Ricoh Theta 相机。 SDK 使用多个 HTTP 请求来触发图像的捕获并从相机下载图像。

SDK 在内部使用信号量来同步请求,但自从升级到 iOS 10 后,由于某种原因,这似乎不再起作用。根据开发者论坛的说法,这个问题是已知的,但理光显然并不在意。

我将其缩小到 SDK 中的特定部分,SDK 通过 RunLoop 每 0.5 秒发送一次请求,循环检查图像是否可用。

这发生在这个函数中:

/**
* Start status monitoring
* @param command ID of command to be monitored
* @return Status indicating completion or error
*/
- (NSString*)run:(NSString*)command
{
// Create and keep HTTP session
NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration];
_session= [NSURLSession sessionWithConfiguration:config];

_commandId = command;

// Semaphore for synchronization (cannot be entered until signal is called)
_semaphore = dispatch_semaphore_create(0);

// Create and start timer
NSTimer *timer = [NSTimer timerWithTimeInterval:0.5f
target:self
selector:@selector(getState:)
userInfo:nil
repeats:YES];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
[runLoop run];

// Wait until signal is called
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"HERE");
return _state;
}

问题是 NSLog(@"HERE");即使在 getState 方法 ( dispatch_semaphore_signal(_semaphore); ) 中触发了 Semaphore,也永远不会调用。通过单步调试器中的行,我可以肯定地知道这一点。
/**  
* Delegate called during each set period of time
* @param timer Timer
*/
- (void)getState:(NSTimer*)timer {
// Create JSON data
NSDictionary *body = @{@"id": _commandId};

// Set the request-body.
[_request setHTTPBody:[NSJSONSerialization dataWithJSONObject:body options:0 error:nil]];

// Send the url-request.
NSURLSessionDataTask* task =
[_session dataTaskWithRequest:_request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!error) {
NSArray* array = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
_state = [array valueForKey:@"state"];
_fileUri = [array valueForKeyPath:@"results.fileUri"];
NSLog(@"result: %@", _state);
} else {
_state = @"error";
NSLog(@"GetStorageInfo: received data is invalid.");
}
if (![_state isEqualToString:@"inProgress"]) {
[timer invalidate];
dispatch_semaphore_signal(_semaphore);


// Stop timer
[timer invalidate];
}
}];
[task resume]; }

我已经看到 iOS 10 中的 NSRunLoop 发生了一些细微的变化,这可能与我在这里面临的情况有关吗?我得到的另一种感觉是,iOS 可能无法获得将调用 dispatch_semaphore_wait 的新线程。 .

在过去的几个小时里,我一直在用头撞 table ,真的希望你们中的一个可以在这里帮忙!

最佳答案

这与信号量无关。

documentation for -[NSRunLoop run] 说:

Puts the receiver into a permanent loop, during which time it processes data from all attached input sources. […]

If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.

Manually removing all known input sources and timers from the run loop is not a guarantee that the run loop will exit. macOS can install and remove additional input sources as needed to process requests targeted at the receiver’s thread. Those sources could therefore prevent the run loop from exiting.

If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:

BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);

where shouldKeepRunning is set to NO somewhere else in the program.



特别注意我加粗的句子。没有理由期望 -run: 中的任何代码 [runLoop run] 之后的方法call 将永远被执行。

似乎最初的程序员希望使计时器无效会删除保持运行循环运行的最后一个输入,并且它将从它的 -run返回。方法。这种希望是不合理的。它在某些情况下在某些版本的操作系统上工作的事实是不幸的巧合。 (不幸的是,如果它没有奏效,他们会意识到自己的错误并找到不同的方法。)

如果您尝试使用 Apple 建议的解决方法,请注意它仅在输入源(而不是计时器)设置为 shouldKeepRunning 时才有效。为假。这是因为,据记录, -runMode:beforeDate:计时器触发时不一定会返回。因此,您需要使用输入源来触发退出。

您可以使用 NSPort “戳”运行循环。您可以使用 -performSelector:onThread:withObject:waitUntilDone: ,针对正在运行循环的线程。你不能做跨线程 NSRunLoop访问,但您可以下拉到 CFRunLoop API,是线程安全的,通过 -getCFRunLoop .然后,您可以使用 CFRunLoopPerformBlock()CFRunLoopWakeUp()运行一个设置 shouldKeepRunning 的 block 为假。 -run:方法应该 CFRetain() CFRunLoop然后是调用 CFRunLoopWakeUp() 的代码应该 CFRelease()它,只是为了确保它可以根据需要生存。否则,在原始线程和 CFRunLoopWakeUp() 内的任何最终工作之间存在潜在的竞争条件。 .

综上所述,您可以通过使用 GCD 调度计时器源而不是 NSTimer 来替换其中的大部分内容。然后停止使用 NSRunLoop东西。这取决于是否 -getState:确实需要在与 -run: 相同的线程上运行.使用当前代码,它确实在同一个线程上运行。使用调度计时器源,它不会。仅使用部分代码很难说,但在我看来,使用调度计时器源应该没有任何问题。

关于ios - 自从升级到 iOS10 后,带有 NSRunLoop 的信号量无法正常工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39649088/

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