Search code examples
iosxcodensoperationnsoperationqueue

Concurrent NSOperation and how to set isFinished and isExecuting?


I am trying to split up my programs flow using NSOperations. I am using the Parse framework to make a simple messaging app. I want to display some messages and then delete them. The display messages shouldn't be called unless the delete operations is finished so I want to try using an NSQueue and add a displayMessages operation to it and then a deleteMessages operation (named MyOperation below). I understand that concurrent operations means they will only execute one after another in a queue fashion. Below is my code for the delete method. Is there a way to manually tell the operation it is finished i.e. setting isFinished or isExecuting??

// MyOperation.h
@interface MyOperation : NSOperation {
@property (strong, nonatomic) NSMutableArray *toDelete;
}
@end
And the implementation:

// MyOperation.m
@implementation MyOperation


- (id)initWithArray:(NSMutableArray *)array
{
    self = [super init];
    if (self == nil)
        return nil;

    _toDelete=array;

}

- (void)main {
    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }



//how do I get main to finish execution ONLY after deleteAllInBackground has finished? 


[PFObject deleteAllInBackground:self.toDelete];

    if ([self isCancelled]) {
        NSLog(@"** operation cancelled **");
    }


    NSLog(@"Operation finished");
}


@end

right now this code above won't solve my problem. It will queue up the ops but this one will finish even though the deleteAllInBackground is still running. Would really appreciate some help here! thanks


other possible solution:

-(void)receivedMessage
{
    NSLog(@"push!");
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        [self displayMessages];
        dispatch_async(dispatch_get_main_queue(), ^(void){
            if([self.toDelete count]>0) {
                [PFObject deleteAllInBackground:self.toDelete];

            }


        });
    });



}

Solution

  • I would suggest you to use dispatch_async like below;

    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
        dispatch_async(dispatch_get_main_queue(), ^(void){
            // Display messages
        });
        // Delete messages here
    });
    

    If you have to use NSOperationQueue then I would suggest you to use KVO to get notification for task completion; When you setup your queue, do this:

    [self.deleteQueue addObserver:self forKeyPath:@"delete-operations" options:0 context:NULL];
    

    Then do this in your observeValueForKeyPath:

    - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                         change:(NSDictionary *)change context:(void *)context {
        if (object == self.deleteQueue && [keyPath isEqualToString:@"delete-operations"]) {
            if ([self.queue.operations count] == 0) {
                // Delete operation done
                // Display messages here
            }
        } else {
            [super observeValueForKeyPath:keyPath ofObject:object 
                               change:change context:context];
         }
    }
    

    [EDIT]

    -(void)receivedMessage {
    @synchronized(self) {
        NSLog(@"push!");
        dispatch_async(dispatch_get_main_queue(), ^(void) {
            [self displayMessages];
            dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
                if([self.toDelete count]>0) {
                    // this deletion doesn't matter if background or on main thread as it's already in background queue
                    [PFObject deleteAllInBackground:self.toDelete];
                }
            });
        });
    }
    }