Выполнить блок внутри NSOperation - PullRequest
0 голосов
/ 19 ноября 2011

У меня есть метод в каком-то классе, который выполняет некоторую задачу, используя блок. Когда я выполняю этот метод с помощью 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);
    }
}

Ответы [ 2 ]

0 голосов
/ 27 ноября 2011

Существует два способа решения вашей проблемы:

Самый простой выход

- это, как предполагает Дин, использование +[NSURLConnection sendSynchronousRequest:returningResponse:error:], поскольку вы уже находитесь в другом потоке.Это покрывает - я бы сказал - 80-90% времени, это действительно просто для реализации и Just Works ™.

Другой способ

только немного сложнее и имеетВы рассмотрели все случаи, когда первого метода недостаточно, посетив корень вашей проблемы:

NSURLConnection работает совместно с runloop - и потоки, управляемые NSOperationQueue, не обязательноиспользовать (или даже иметь!) связанный runloop.

При вызове +[NSURLConnection connectionWithRequest:delegate:] неявно создаст runloop, если необходимо, это не заставит runloop фактически запускаться!

Это ваша ответственность , когда 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.

Поскольку вы, вероятно, не хотите, чтобы клиенты вашего загрузчика видели и, возможно, связывались со свойствами, которые я только что предложил добавить, вы можете объявить их в продолжении класса.

0 голосов
/ 23 ноября 2011

Вы выполняете NSConnection асинхронно (что вам не нужно делать в NSOperation, потому что вы уже должны быть в фоновом потоке).

После вашего вызова fetchXMLWithCompletionHandler ваш метод заканчивается.Это говорит о том, что операция NSO завершена, и она освобождается, а ее поток либо повторно используется для чего-то другого, либо, что более вероятно, также освобождается.Это означает, что к тому времени, когда вы получите ваши обратные вызовы, ваш первоначальный объект больше не существует!

Есть два решения:

1) Используйте NSURLConnection синхронно .Это будет ждать в вашем myClassMethod, пока он не получит ответ.

2) Узнайте о параллельном режиме NSOperations .Я не знаю, будет ли это работать с NSInvocationOperation, хотя :( И это довольно сложно по сравнению с вариантом (1).

Я бы использовал метод (1) - вы уже создали фоновый поток для выполнения вашей операциизачем создавать другой для запроса соединения?

...