I am currently using NSOperation (actually, NSInvocationOperation) and 
NSOperationQueue to decouple my GUI from some long running operations.  Some 
operations are dependent on one another, in a purely linear fashion, so I've 
been treating them in the same way I'd treat a singly linked list, keeping 
track of the last operation pushed into the queue.  The problem is that the 
documentation states that we must not modify an operation that is already in 
the queue; I don't know if calling addDependency is going to do something to an 
operation in the queue or not.  I also don't know if my locking trick for the 
delegate guaranteed to work or not (I want to make sure that after I check to 
see that the delegate responds to a particular selector, the delegate isn't 
changed for another delegate before I have a chance to call the selector).  
Finally, I'm not sure that my dealloc method is 100% kosher.

Here is some sample code to try to make this more clear (all of this banged out 
in Outlook, there may be typos).  I've inlined questions in the comments in the 
code:

--- MyOperationInvoker.h ---
@protocol delegateProtocol
@optional                                       // IMPORTANT!
- (oneway void) foo:(int) result;       // Single process; do I need oneway?
- (oneway void) bar:(int) result;       // Single process; do I need oneway?
@end

@interface MyOperationInvoker : NSObject
{
        NSOperationQueue *myQueue;
        NSOperation *lastOperation;
        id delegate;
        NSLock *delegateLock;
}
@property(retain, readwrite) id delegate;
- (void) independentOperation:(id) thing;
- (void) dependentOperation:(id) thing;
@end
--- End of MyOperationInvoker.h ---

--- MyOperationInvoker.m ---
@interface MyOperationInvoker() 
@property(retain, readwrite) NSOperationQueue *myQueue;
@property(retain, readwrite) NSOperation *lastOperation;
@property(retain, readwrite) NSLock *delegateLock;
- (void) invokedIndependentOp:(id)thing;
- (void) invokedDependentOp:(id) thing;
@end

@implementation MyOperationInvoker

@synthesize myQueue;
@synthesize lastOperation;
@synthesize delegateLock;
@dynamic delegate;

- (id) delegate
{
        return [[delegate retain] autorelease];
}

/*
        Note the use of the delegateLock; by using the lock I'm hoping to ensure
        that the delegate can't be changed during a critical section within the
        invoked*() methods below.
 */
- (void) setDelegate:(id) value
{
        if (value != delegate)
        {
                [self.delegateLock lock];
                        [value retain];
                        [delegate release];
                        delegate = value;
                [self.delegateLock unlock];
        }
}

- (id) init
{
        self = [super init];
        if (self != nil)
        {
                self.delegateLock = [[NSLock alloc] init];
                self.delegate = nil;
                self.myQueue = [[NSOperationQueue alloc] init];
                self.lastOperation = nil;
        }
        return self;
}

- (void) dealloc
{
        self.delegate = nil;    // locks the lock while doing this
        [self.myQueue cancelAllOperations];     // delegate == nil, therefore 
no callbacks

        // OK, the $64,000 question is, will the dealloc method of 
NSOperationQueue
        // run on the current thread, or will it run on one of the threads that 
it
        // spawned to run the various operations?  Basically, are the following 
3 calls
        // guaranteed to be executed in-order?
        self.myQueue = nil;
        self.delegateLock = nil;
        [super dealloc];
}

- (void) independentOperation:(id) thing
{
        NSInvocationOperation *operation = 
                [[NSInvocationOperation alloc] initWithTarget:self
                        selector:@selector(invokedIndependentOp:)
                        object:thing];
        [self.myQueue addOperation:operation];
        [operation release];
}

/*
        Am I able to add the dependencies in the order that I'm doing them in?
        Am I allowed to add a dependency when one of the operations is already
        in the run queue?  What happens if self.lastOperation has already
        completed before operation is added to the queue?  What happens if
        self.lastOperation is cancelled before operation has a chance to run?
        Does operation ever run?
 */
- (void) dependentOperation:(id) thing
{       
        NSInvocationOperation *operation = 
                [[NSInvocationOperation alloc] initWithTarget:self
                        selector:@selector(invokedIndependentOp:)
                        object:thing];
        [operation addDependency:self.lastOperation];
        [self.myQueue addOperation:operation];
        self.lastOperation = operation;
        [operation release];
}

/*
        Do the locks here prevent the delegate from being changed while I'm
        in each critical section?  I know that they should, I just want to
        double check that my logic is right...
 */

- (void) invokedIndependentOp:(id)thing
{
        int result = // Some long running operation
        [self.delegateLock lock];
        if ([self.delegate respondsToSelector:@selector(foo:)])
        {
                [self.delegate foo:result];
        }
        [self.delegateLock unlock];
}

- (void) invokedDependentOp:(id) thing
{       
        int result = // Some long running operation
        [self.delegateLock lock];
        if ([self.delegate respondsToSelector:@selector(bar:)])
        {
                [self.delegate bar:result];
        }
        [self.delegateLock unlock];
}

@end
--- End of MyOperationInvoker.m ---

Thanks for any pointers,
Cem Karan
_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to