0

ブロックを使用して何らかのタスクを実行するクラスのメソッドがあります。NSInvocationOperation を使用してそのメソッドを実行すると、制御がブロックに移動することはありません。ブロック内でログを記録しようとしましたが、実際には呼び出されません。しかし、単にそのクラスのインスタンスでそのメソッドを呼び出すと、すべてが期待どおりに機能します。

ブロックは NSOperation 内で実行されませんか?

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:myClassObj selector:@selector(myClassMethod:) object:obj1];
[[AppDelegate sharedOpQueue] addOperation:op];
[op release];


- (void)myClassMethod:(id)obj
{
    AnotherClass *otherClass = [[AnotherClass allco] init]
    [otherClass fetchXMLWithCompletionHandler:^(WACloudURLRequest* request, xmlDocPtr doc, NSError* error)
     {
         if(error){
             if([_delegate respondsToSelector:@selector(handleFail:)]){
                 [_delegate handleFail:error];
             }
             return;
         }

         if([_delegate respondsToSelector:@selector(doSomeAction)]){
             [_delegate doSomeAction];
         }
     }];

}

- (void) fetchXMLWithCompletionHandler:(WAFetchXMLHandler)block
{
    _xmlBlock = [block copy];
    [NSURLConnection connectionWithRequest:request delegate:self];
}

-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if(_xmlBlock) {
        const char *baseURL = NULL;
        const char *encoding = NULL;

        xmlDocPtr doc = xmlReadMemory([_data bytes], (int)[_data length], baseURL, encoding, (XML_PARSE_NOCDATA | XML_PARSE_NOBLANKS)); 

        NSError* error = [WAXMLHelper checkForError:doc];

        if(error){
            _xmlBlock(self, nil, error);
        } else {
            _xmlBlock(self, doc, nil);
        }

        xmlFreeDoc(doc);
    }
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    if(_xmlBlock) {
        _xmlBlock(self, nil, error);
    }
}
4

2 に答える 2

0

問題を解決するには、次の 2 つの方法があります。

簡単な方法

ディーンが示唆するよう+[NSURLConnection sendSynchronousRequest:returningResponse:error:]に、すでに別のスレッドにいるので、を使用しています。これで、80 ~ 90% の確率で対応できます。実装は非常に簡単で、Just Works™ です。

反対に

問題の根本にアクセスすることで、最初の方法では不十分なすべてのケースをカバーしました。

NSURLConnectionは runloop と連携して動作します — によって管理されるスレッドはNSOperationQueue、関連する runloop を必ずしも使用するとは限りません (または持っているとは限りません!)。

呼び出し+[NSURLConnection connectionWithRequest:delegate:]によって実行ループが暗黙的に作成されますが、必要に応じて実行ループが実際に実行されるわけではありません。

NSOperationQueue使用する がメイン スレッドに関連付けられたキューでない場合、これはユーザーの責任です。

これを行うには、 の実装をfetchXMLWithCompletionHandler:次のように変更します。

- (void)fetchXMLWithCompletionHandler:(WAFetchXMLHandler)block
{
    self.xmlHandler = block; // Declare a @property for the block with the copy attribute set

    self.mutableXMLData = [NSMutableData data]; // again, you should have a property for this...

    self.currentConnection = [NSURLConnection connectionWithRequest:request delegate:self]; // having a @property for the connection allows you to cancel it, if needed.

    self.connectionShouldBeRunning = YES; // ...and have a BOOL like this one, setting it to NO in connectionDidFinishLoad: and connection:didFailWithError:

    NSRunLoop *loop = [NSRunLoop currentRunLoop];
    NSDate *neverExpire = [NSDate distantFuture];

    BOOL runLoopDidIterateRegularly = YES;
    while( self.connectionShouldBeRunning && runLoopDidIterateRegularly ) {
        runLoopDidIterateRegularly = [loop runMode:NSDefaultRunLoopMode beforeDate:neverExpire];
    }
}

これらの小さな変更により、準備完了です。おまけ:これは非常に柔軟で、すべてのコードで (最終的には) 再利用可能です。XML 解析をそのクラスから移動し、ハンドラーが単純にNSData、 、NSErrorおよび (オプションで) を取得する場合NSURLResponse

ローダーのクライアントに、私が追加するよう提案したばかりのプロパティを見たり、混乱させたりしたくないので、クラスの継続でそれらを宣言することができます。

于 2011-11-27T00:18:25.503 に答える
0

NSConnection を非同期的に実行しています (既にバックグラウンド スレッドにいる必要があるため、NSOperation で実行する必要はありません)。

を呼び出した後fetchXMLWithCompletionHandler、メソッドは終了します。これは、NSOperation が終了して解放され、そのスレッドが別の目的で再利用されるか、おそらく解放されることを示します。これは、コールバックを取得するまでに、最初のオブジェクトがもう存在しないことを意味します!

2 つの解決策があります。

1) NSURLConnection を同期的に使用します。myClassMethodこれは、応答があるまで待機します。

2) NSOperations の並行モードについて学びます。ただし、これが NSInvocationOperation で機能するかどうかはわかりません:(そして、オプション(1)に比べてかなり複雑です。

私は方法 (1) を使用します - 操作を実行するためのバックグラウンド スレッドを既に作成していますが、接続要求を実行するためにわざわざ別のスレッドを作成する必要はありません。

于 2011-11-23T13:23:06.143 に答える