ios - 如何在 NSOperationQueue 上使用 NSRunLoop?
<p><p>我有一个通过蓝牙与 ExternalAccessory 通信的应用,响应有一些延迟,所以我希望 IO 在后台线程上发生。</p>
<p>我为单线程操作设置了一个 NSOperationQueue 以将我的请求排入队列:</p>
<pre><code>self.sessionQueue = ;
self.sessionQueue.maxConcurrentOperationCount = 1;
</code></pre>
<p>如果我计划读取和写入来自该队列的 <code>EAAccessory</code> 流,我的应用程序会崩溃,因为如果线程上没有 <code>NSRunLoop</code> 则无法传递来自套接字的数据队列正在使用。初始化队列后,我立即创建了一个带有空 <code>NSMachPort</code> 的运行循环,以保持它运行并启动它:</p>
<pre><code>[self.sessionQueue addOperationWithBlock:^{
NSRunLoop* queueLoop = ;
forMode:NSRunLoopCommonModes];
; // <- this blocks
}];
</code></pre>
<p>这会阻塞队列,因为运行循环永远不会退出,但我不确定如何正确管理运行循环,以便我可以成功地从附属流中读取。</p></p>
<br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
<p><p>您不应该尝试在 <code>NSOperation</code> 中运行运行循环。 Grand Central Dispatch 拥有运行该操作的线程。您应该启动自己的线程并将其运行循环用于 session 流。</p>
<p> <a href="https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW26" rel="noreferrer noopener nofollow">However, you need to be aware that <code>NSRunLoop</code> is not generally thread safe, but <code>CFRunLoop</code> is.</a>这意味着当您想在 session 处理线程上运行一个 block 时,您需要下拉到 <code>CFRunLoop</code> 级别。</p>
<p>此外,获得对后台线程运行循环的引用的唯一方法是在该后台线程上运行某些东西。所以第一步是创建自己的 <code>NSThread</code> 子类来导出自己的运行循环:</p>
<pre><code>typedef void (^MyThreadStartCallback)(CFRunLoopRef runLoop);
@interface MyThread: NSThread
/// After I'm started, I dispatch to the main queue to call `callback`,
// passing my runloop. Then I destroy my reference to `callback`.
- (instancetype)initWithCallback:(MyThreadStartCallback)callback;
@end
@implementation MyThread {
MyThreadStartCallback _callback;
}
- (instancetype)initWithCallback:(MyThreadStartCallback)callback {
if (self = ) {
_callback = callback;
}
return self;
}
- (void)main {
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
_callback(runLoop);
});
_callback = nil;
CFRunLoopRun();
}
@end
</code></pre>
<p>现在您可以创建一个 <code>MyThread</code> 的实例,并传入一个回调。当你启动 <code>MyThread</code> 时,它会使回调在主线程上运行,并将它自己的(<code>MyThread</code> 的)运行循环传递给回调。因此,您可以使用 <code>MyThread</code> 作为 session 处理线程,如下所示:</p>
<pre><code>@implementation Thing {
CFRunLoopRef _sessionRunLoop;
}
- (void)scheduleStreamsOfSession:(EASession *)session {
MyThread *thread = [ initWithCallback:^(CFRunLoopRef runLoop) {
// Here I'm on the main thread, but the session-handling thread has
// started running and its run loop is `runLoop`.
;
}];
;
}
- (void)scheduleStreamsOfSession:(EASession *)session inRunLoop:(CFRunLoopRef)runLoop {
// Here I'm on the main thread. I'll save away the session-handling run loop
// so I can run more blocks on it later, perhaps to queue data for writing
// to the output stream.
_sessionRunLoop = runLoop;
NSInputStream *inputStream = session.inputStream;
NSOutputStream *outputStream = session.outputStream;
// Here I'm on the main thread, where it's not safe to use the
// session-handling thread's NSRunLoop, so I'll send a block to
// the session-handling thread.
CFRunLoopPerformBlock(runLoop, kCFRunLoopCommonModes, ^{
// Here I'm on the session-handling thread, where it's safe to
// use NSRunLoop to schedule the streams.
NSRunLoop *currentRunLoop = ;
;
;
});
// CFRunLoopPerformBlock does **not** wake up the run loop. Since I want
// to make sure the block runs as soon as possible, I have to wake up the
// run loop manually:
CFRunLoopWakeUp(_sessionRunLoop);
}
@end
</code></pre></p>
<p style="font-size: 20px;">关于ios - 如何在 NSOperationQueue 上使用 NSRunLoop?,我们在Stack Overflow上找到一个类似的问题:
<a href="https://stackoverflow.com/questions/36364179/" rel="noreferrer noopener nofollow" style="color: red;">
https://stackoverflow.com/questions/36364179/
</a>
</p>
页:
[1]