実行ループをNSOperation
の内部で実行しないでください。 Grand Central Dispatchは、操作が実行されているスレッドを所有しています。独自のスレッドを起動し、セッションストリームにその実行ループを使用する必要があります。
However, you need to be aware that NSRunLoop
is not generally thread safe, but CFRunLoop
is.これは、あなたがあなたのセッションハンドリングスレッドでブロックを実行したいときCFRunLoop
レベルまで低下する必要があることを意味します。
また、バックグラウンドスレッドの実行ループへの参照を取得する唯一の方法は、そのバックグラウンドスレッドで何かを実行することです。
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
のインスタンスを作成することができますので、ステップ1は、独自の実行ループをエクスポートし、独自のNSThread
サブクラスを作成することです。 MyThread
を起動すると、そのコールバックがメインスレッドで実行され、コールバックに自身のループ(MyThread
')が渡されます。ですから、このようなセッションハンドリングスレッドとしてMyThread
を使用することができます。必要に応じていずれかのスレッドがそれのために作成したNSRunLoop
を持つことができます
@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
」。しかし、実行ループを実行しているNSOperationブロックを有するNSOperationQueuesのスマートな使用ではありません「;同様に、単に(実行ループをそのまま実行している)NSOperationにすべてのスレッドコードを移動し、操作キューにそれをスローすることはありません。 -https://lists.apple.com/archives/cocoa-dev/2009/Sep/msg01145.html – alfwatt
例:https://horseshoe7.wordpress.com/2015/04/29/nsoperation-and-nsrunloop- - 結婚・オブ・必要性/ – alfwatt