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

您正在异步执行 NSConnection (您不需要在 NSOperation 中执行此操作,因为您应该已经在后台线程上)。

在您调用 后fetchXMLWithCompletionHandler,您的方法结束。这表明 NSOperation 已完成并被释放,它的线程要么被重用于其他东西,要么更可能被释放。这意味着当您收到回调时,您的初始对象不再存在!

有两种解决方案:

1)同步使用 NSURLConnection。这将在您myClassMethod收到回复之前等待。

2)了解NSOperations的并发模式。我不知道这是否适用于 NSInvocationOperation :( 与选项 (1) 相比,它相当复杂。

我会使用方法 (1) - 您已经创建了一个后台线程来执行您的操作,为什么还要创建另一个来执行您的连接请求?

于 2011-11-23T13:23:06.143 回答
0

有两种方法可以解决您的问题:

最简单的出路

是 - 正如 Dean 建议的那样 - 使用+[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 解析移出该类并让你的处理程序简单地采用 an NSData, anNSError和(可选)an NSURLResponse

由于您可能不希望加载程序的客户端看到并可能弄乱我刚刚建议您应该添加的属性,因此您可以在类延续中声明它们。

于 2011-11-27T00:18:25.503 回答