ここで一種の問題が発生しています。ファイルを読み取り、そのコンテンツを UITableView に表示するアプリを開発しています。最近、ファイルが非常に大きくなる可能性があり、ファイルの実際の読み取りを非同期でコーディングする必要があることに気付きました。私のプロジェクトはすでにかなり大きく、今日、すでに持っていたコードを NSOperation にラップすることができました。
私がしたことは、パーサー (ファイルを開いて読み取る) が NSOperation 内で呼び出されるようになったことです。そのように:
@implementation ReadPcapOperation
@synthesize parser =_parser;
- (id) initWithURL:(NSURL *)url linkedTo:(PacketFlowViewController *)packetController
{
self = [super init];
if (self) {
_parser = [[PcapParser alloc] initWithURL:url linkedTo:packetController];
}
return self;
}
- (void)main {
// a lengthy operation
@autoreleasepool {
if (self.isCancelled)
return;
[_parser read];
}
}
@end
ここでは実装のみを示します。.h ファイルには重要なことは何もありません。この NSOperation サブクラスは、 NSOperation 内で呼び出されます。
_queue = [NSOperationQueue new];
_queue.name = @"File Parsing Queue";
_queue.maxConcurrentOperationCount = 1;
[_queue addOperation:_readFileOperation];
_readFileOperation は、上記の ReadPcapOperation のインスタンスです。
今、コードをテストしても違いはありません。ファイルを開くと、ファイルのコンテンツが UITableView にロードされている間、UI がまだブロックされています。私はこの条件でテストしました:
[NSThread isMainThread]
このテストは、ReadPcapOperation からメインでNOを返します。これは、まさに私が必要としているものです。しかし、このテストは、「read」メソッド内に置くとYESを返します。メッセージは、 ReadPcapOperation から main 内のオブジェクトに送信されます。したがって、私のコード全体はまだメインスレッドで実行されており、UI をブロックしています。
ここで何が欠けていますか?
さらに説明が必要な場合はお知らせください。
編集 :
奇妙なのは次のとおりです。バックグラウンド スレッドで実行されるはずのコードの一部を投稿します。
- (void) read
{
if ([NSThread isMainThread])
NSLog(@"read: IT S MAIN THREAD");
else
NSLog(@"read: IT S NOT MAIN THREAD");
[_fileStream open];
}
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode)
{
case NSStreamEventOpenCompleted:
{
//We read the pcap file header
[self readGlobalHeader];
[self readNextPacket];
break;
}
case NSStreamEventHasBytesAvailable:
{
//We read all packets
[self readNextPacket];
break;
}
case NSStreamEventNone:
{
break;
}
case NSStreamEventHasSpaceAvailable:
{
break;
}
case NSStreamEventEndEncountered:
{
NSLog(@"End encountered !");
[_fileStream close];
[_fileStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
//_fileStream = nil;
break;
}
case NSStreamEventErrorOccurred:
{
NSError *theError = [stream streamError];
NSLog(@"Error %i stream event occured. Domain : %@.", theError.code, theError.domain);
[stream close];
break;
}
}
}
- (void) readGlobalHeader
{
if ([NSThread isMainThread])
NSLog(@"readGlobalHeader: IT S MAIN THREAD");
else
NSLog(@"readGlobalHeader: IT S NOT MAIN THREAD");
int sizeOfGlobalHeader = 24;
ここに、NSOperation から直接呼び出すメソッド read が表示されます。そこのログには、「メインスレッドではありません」と書かれています。ここまでは順調ですね。read は NSInputStreamObject を開き、デリゲートは「handleEvent」を呼び出します。この時点でバイトを読み取ります。「readGlobalHeader」を呼び出してファイルの最初のバイトを読み取ると、ログは「MAIN THREAD」になります。バックグラウンドスレッドにもあるべきではありませんか?私は本当にそこで迷子になりました!
注意すべき重要な点は、ストリームを初期化するときに、「読み取り」を呼び出す前に、次のコード行でストリームを設定したことです (それが原因かどうかはわかりません)。
[_fileStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
編集 2: ブレークポイントの後のバックトレースは次のとおりです。動作は、上で説明したとおりです。(画像を投稿するのに十分な評判ではありません)