您不应该尝试在NSOperation
内部运行循环。 Grand Central Dispatch拥有正在运行该操作的线程。你应该开始你自己的线程并为你的会话流使用它的运行循环。
However, you need to be aware that NSRunLoop
is not generally thread safe, but CFRunLoop
is.这意味着当你想在你的会话处理线程上运行一个块时,你需要下降到CFRunLoop
级别。
此外,获得对后台线程运行循环的引用的唯一方法是在后台线程上运行某些内容。所以第一步是创建自己的NSThread
子类,出口自身的运行循环:
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 = [super init]) {
_callback = callback;
}
return self;
}
- (void)main {
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
dispatch_async(dispatch_get_main_queue(), ^{
_callback(runLoop);
});
_callback = nil;
CFRunLoopRun();
}
@end
现在你可以创建一个MyThread
例如,传递一个回调。当您启动MyThread
时,它将使该回调在主线程上运行,并且它会将其自己的(MyThread
)运行循环传递给回调。所以,你可以使用一个MyThread
为您的会话处理线程,就像这样:
@implementation Thing {
CFRunLoopRef _sessionRunLoop;
}
- (void)scheduleStreamsOfSession:(EASession *)session {
MyThread *thread = [[MyThread alloc] initWithCallback:^(CFRunLoopRef runLoop) {
// Here I'm on the main thread, but the session-handling thread has
// started running and its run loop is `runLoop`.
[self scheduleStreamsOfSession:session inRunLoop:runLoop];
}];
[thread start];
}
- (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 = [NSRunLoop currentRunLoop];
[inputStream scheduleInRunLoop:currentRunLoop forMode:NSRunLoopCommonModes];
[outputStream scheduleInRunLoop:currentRunLoop forMode:NSRunLoopCommonModes];
});
// 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
“如果因为要获取通知或类似的API承诺而运行循环,那么您需要小心,不能仅仅消除该循环的运行并让事情继续工作。同样,不要将所有线程代码移动到NSOperation中(运行循环完好无损地运行)并将其放入操作队列中;让NSOperation块运行运行循环并不是NSOperationQueues的明智之举。“ -https://lists.apple.com/archives/cocoa-dev/2009/Sep/msg01145.html – alfwatt
示例:https://horseshoe7.wordpress.com/2015/04/29/nsoperation-and-nsrunloop-结婚的必要/ – alfwatt