NSOperationQueue становится приостановлено наугад? - PullRequest
2 голосов
/ 20 ноября 2011

У меня есть приложение, которое интенсивно использует NSOperationQueue.

Иногда я замечал, что некоторые из NSOperationQueues будут "блокироваться" или переходить в состояние "isSuspended" случайным образом, даже если мой код никогда не вызывает setSuspended:Метод.

Невозможно воспроизвести и очень трудно отладить, потому что всякий раз, когда я подключаю устройство к XCode для его отладки, приложение перезагружается и ошибка исчезает.

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

Глядя на системные журналы устройства, я обнаружил, что [myOperationQueue operationCount]будет увеличиваться, но операции в очереди не будут выполняться.

Я еще не пробовал вручную устанавливать "setSuspended: NO", но действительно ли это необходимо?

Что может быть причиной этого?

Вот немного моего кода

Контроллер представления, который вызывает операции

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
        self.operationQueue = [[[NSOperationQueue alloc] init] autorelease];
        [self.operationQueue setMaxConcurrentOperationCount:2];

        self.sendOperationQueue = [[[NSOperationQueue alloc] init] autorelease];
        [self.sendOperationQueue setMaxConcurrentOperationCount:2];

        self.receiveOperationQueue = [[[NSOperationQueue alloc] init] autorelease];
        [self.receiveOperationQueue setMaxConcurrentOperationCount:1];
    }
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    [operationQueue cancelAllOperations];
    [sendOperationQueue cancelAllOperations];
    [receiveOperationQueue cancelAllOperations];

    [operationQueue release];
    [sendOperationQueue release];
    [receiveOperationQueue release];
}

- (IBAction)sendMessage
{
    if(![chatInput.text isEqualToString:@""])
    {
        NSString *message = self.chatInput.text;
        SendMessageOperation *sendMessageOperation = [[SendMessageOperation alloc] initWithMatchData:matchData andMessage:message resendWithKey:nil];
        [self.sendOperationQueue addOperation:sendMessageOperation];
        [sendMessageOperation release];
    }
}

NSOperation подкласс SendMessageOperation

- (id)initWithMatchData:(MatchData*)data andMessage:(NSString*)messageString resendWithKey:(NSString*)resendKey
{
self = [super init];

    if(self != nil)
    {
        if(data == nil || messageString == nil)
        {
            [self release];
            return nil;
        }
        appDelegate = (YongoPalAppDelegate *) [[UIApplication sharedApplication] delegate];

        context = [[NSManagedObjectContext alloc] init];
        [context setPersistentStoreCoordinator:[appDelegate persistentStoreCoordinator]];
        [context setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeContextChanges:) name:NSManagedObjectContextDidSaveNotification object:context];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeMainContextChanges:) name:NSManagedObjectContextDidSaveNotification object:appDelegate.managedObjectContext];

        self.matchData = (MatchData*)[context objectWithID:[data objectID]];
        matchNo = [[matchData valueForKey:@"matchNo"] intValue];
        partnerNo = [[matchData valueForKey:@"partnerNo"] intValue];

        self.message = messageString;
        self.key = resendKey;

        apiRequest = [[APIRequest alloc] init];
    }
    return self;
}

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];

    self.matchData = nil;
    self.message = nil;
    self.key = nil;
    [context release];
    [apiRequest release];
    [super dealloc];
}

- (void)start
{
    if([self isCancelled] == YES)
    {
        [self willChangeValueForKey:@"isFinished"];
        finished = YES;
        [self didChangeValueForKey:@"isFinished"];
        return;
    }
    else
    {
        [self willChangeValueForKey:@"isExecuting"];
        executing = YES;
        [self main];
        [self didChangeValueForKey:@"isExecuting"];
    }
}

- (void)main
{
    @try
    {
        NSAutoreleasePool *pool = [NSAutoreleasePool new];

        bool taskIsFinished = NO;
        while(taskIsFinished == NO && [self isCancelled] == NO)
        {
            NSDictionary *requestData = nil;

            if(key == nil)
            {
                requestData = [self sendMessage];
            }
            else
            {
                requestData = [self resendMessage];
            }

            NSDictionary *apiResult = nil;
            if(requestData != nil)
            {
                apiResult = [self sendMessageToServer:requestData];
            }

            if(apiResult != nil)
            {                
                [[NSNotificationCenter defaultCenter] postNotificationName:@"shouldConfirmSentMessage" object:nil userInfo:apiResult];
            }

            taskIsFinished = YES;
        }

        [self willChangeValueForKey:@"isFinished"];
        [self willChangeValueForKey:@"isExecuting"];
        finished = YES;
        executing = NO;
        [self didChangeValueForKey:@"isFinished"];
        [self didChangeValueForKey:@"isExecuting"];

        [pool drain];
    }
    @catch (NSException *e)
    {
        NSLog(@"Exception %@", e);
    }
}

- (BOOL)isConcurrent
{
    return YES;
}

- (BOOL)isFinished
{
    return finished;
}

- (BOOL)isExecuting
{
    return executing;
}

- (void)mergeContextChanges:(NSNotification *)notification
{
    [[NSNotificationCenter defaultCenter] postNotificationName:@"mergeChatDataChanges" object:nil userInfo:[notification userInfo]];
}

- (void)mergeMainContextChanges:(NSNotification *)notification
{
    NSSet *updated = [[notification userInfo] objectForKey:NSUpdatedObjectsKey];
    for(NSManagedObject *thing in updated)
    {
        [[context objectWithID:[thing objectID]] willAccessValueForKey:nil];
    }

    [context mergeChangesFromContextDidSaveNotification:notification];
}

Ответы [ 3 ]

2 голосов
/ 20 ноября 2011

Вы используете:

setMaxConcurrentOperationCount:X

Я догадываюсь, что операции X в очереди не завершены, поэтому любые последующие операции, добавленные в очередь, не будут выполняться, пока это не так.

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

Из документации isSuspended метода NSOperationQueue:

Если вы хотите знать, когда изменяется состояние приостановки очереди, настройте наблюдателя KVO для наблюдения за приостановленным путем ключа операции.queue.

Другая идея, которую вы можете реализовать параллельно с наблюдателем, заключается в создании подкласса NSOperationQueue, который переопределяет метод setSuspended:.В переопределенной версии вы можете установить точку останова, если вы работаете в отладчике, или распечатать трассировку стека в журнал, если вы работаете без отладчика.

Надеюсь, это поможет.

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

взгляните на ASIHTTPRequestConfig.h, там есть несколько флагов, которые вы можете включить, чтобы увидеть, что на самом деле происходит за кулисами.

...