0

ここで一種の問題が発生しています。ファイルを読み取り、そのコンテンツを 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: ブレークポイントの後のバックトレースは次のとおりです。動作は、上で説明したとおりです。(画像を投稿するのに十分な評判ではありません)

read メソッドでの ブレークポイント ストリームが開いた後のブレークポイント

4

5 に答える 5