NSOperationQueue 随机暂停?

发布于 2024-12-16 21:30:45 字数 5782 浏览 2 评论 0原文

我有一个大量使用 NSOperationQueue 的应用程序。

有时我注意到一些 NSOperationQueues 会“锁定”或随机进入“isSuspished”状态,即使我的代码从未调用 setSuspished: 方法。

这是不可能复制的,而且很难调试,因为每当我将设备连接到 Xcode 进行调试时,应用程序就会重新加载,错误就会消失。

我在所有可能出现问题的点添加了很多 NSLog,并且只需要使用该应用程序几天,直到错误再次出现。

查看设备系统日志,我发现 [myOperationQueue operationCount] 会增加,但队列中的操作不会执行。

我还没有尝试手动设置“setSuspished: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];
}

I have an app that uses NSOperationQueue intensively.

Sometimes I've noticed that some of the NSOperationQueues would "lock up" or enter a "isSuspended" state randomly even though my code never calls the setSuspended: method.

It's impossible to replicate and very hard to debug because whenever I hook the device up to Xcode to debug it, the app would reload and the bug would go away.

I added a lot of NSLogs at all possible points that might have a problem and just had to use the app for a few days until the bug reemerged.

Looking at the device system logs, I've discovered that [myOperationQueue operationCount] would increment but the opeations in queue will not execute.

I haven't tried manually setting "setSuspended:NO" yet but is that really necessary?

What could be causing this?

Here is a little bit of my code

The view controller that calls operations

- (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 subclass 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];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

黑凤梨 2024-12-23 21:30:45

您正在使用:

setMaxConcurrentOperationCount:X

我的预感是队列中的 X 个操作尚未完成,因此导致添加到队列中的任何后续操作在这种情况之前不会运行。

You're using:

setMaxConcurrentOperationCount:X

My hunch is that X operations in the queue have not finished, therefore causing any subsequent operations added to the queue to not run until this is the case.

风铃鹿 2024-12-23 21:30:45

来自 NSOperationQueue 的 isSuspended 方法文档:

如果想知道队列的挂起状态何时发生变化,可以配置一个KVO观察者来观察操作队列的挂起键路径。

您可以与观察者并行实现的另一个想法是创建一个 NSOperationQueue 的子类来重新定义 setSuspished: 方法。在重新定义的版本中,如果您在调试器上运行,则可以设置断点;如果您在不带调试器的情况下运行,则可以将堆栈跟踪打印到日志中。

希望这有帮助。

From the NSOperationQueue's isSuspended method documentation:

If you want to know when the queue’s suspended state changes, configure a KVO observer to observe the suspended key path of the operation queue.

Another idea that you can implement in parallel with the observer is to create a subclass of NSOperationQueue that redefines the setSuspended: method. In the redefined version you can set a breakpoint if you are running on the debugger, or print a stacktrace to the log if you are running w/o debugger.

Hope this helps.

酒与心事 2024-12-23 21:30:45

看一下 ASIHTTPRequestConfig.h,它有一些标志,您可以打开它们来查看幕后真正发生的情况。

take a look at ASIHTTPRequestConfig.h, it has some flags that you can turn on to see what is really happening behind the scenes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文