Julian Ceipek
Julian Ceipek

Reputation: 524

Handling data returned from multiple NSOperation objects in an NSOperation object that depends on them

I am writing a web-connected application that needs to execute several asynchronous requests to load data needed lower down in the dependency tree.

Fig 1.


For visualization purposes, consider an example with ASIHTTPRequests A,B,C,D,E, and F:

A's url depends on the result of B and C,

and B's url depends on the result of D, E, and F.

B and C can be computed concurrently, and so can D, E, and F.

NSOperationQueue = [(D,E,F),(B,C),A]


Thus far, I have created an NSOperationQueue that contains a dependency tree of ASIHTTPRequests. However, the URLs of the ASIHTTPRequests should depend on the results of the previous operations, and, right now, they do not.

The question: What is the best way to pass the results of the computations performed by multiple NSOperations to the NSOperation that depends on them, and how can I set this up with ASIHTTPRequests?

Thanks in advance, Julian Ceipek

Upvotes: 2

Views: 1244

Answers (2)

Julian Ceipek
Julian Ceipek

Reputation: 524

I ended up solving this problem by wrapping the ASIHTTPRequest in a custom NSOperation object that populated the request in such a way that custom request B contained a pointer to an object in D, E, and F's ASIHTTPRequest UserInfo Dictionary. While I liked @JosephH's solution, I couldn't figure out how to easily generate a dictionary or array with dependency tree intact.

A simplified version of my custom NSOperationObject is provided below; any suggestions are welcome. I used Apple's Concurrency Programming Guide extensively as a reference, but as I have not had any prior experience extending the NSOperation class, I am sure that there is a better way to do this.

#import <Foundation/Foundation.h>
#import "SyncableData.h"
#import "ASIHTTPRequest.h"

@interface PushContentRequest : NSOperation <ASIHTTPRequestDelegate> {
    BOOL executing;
    BOOL finished;
    id <SyncableData> data;
    ASIHTTPRequest *request;
    NSURL *url;
    id <ASIHTTPRequestDelegate> delegate;
}

- (id)initWithDataObject:(id <SyncableData>)theData url:(NSURL *)theUrl delegate:(id <ASIHTTPRequestDelegate>)theDelegate;

@end


#import "PushContentRequest.h"

@implementation PushContentRequest

- (id)initWithDataObject:(id <SyncableData>)theData url:(NSURL *)theUrl delegate:(id <ASIHTTPRequestDelegate>)theDelegate {
    if ((self = [super init])) {
        executing = NO;
        finished = NO;
        data = [theData retain];
        url = [theUrl retain];
        delegate = [theDelegate retain];
    }
    return self;
}

- (BOOL)isConcurrent {
    return YES;
}

- (BOOL)isExecuting {
    return executing;
}

- (BOOL)isFinished {
    return finished;
}

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

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    request = [ASIHTTPRequest requestWithURL:url];
    NSString *xmlToPost = [[NSString alloc] initWithString: [theData getXMLRep]];
    [request appendPostData:[xmlToPost dataUsingEncoding:NSUTF8StringEncoding]];
    [request setDelegate:self];
    NSDictionary *userInfoDict = [[NSDictionary alloc] initWithObjectsAndKeys:data, @"theData", nil];
    [request setUserInfo:userInfoDict];
    [userInfoDict release];
    [xmlToPost release];
    [self willChangeValueForKey:@"isExecuting"];
    [request start];
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];
    [pool release];

}

- (void)dealloc {
    [delegate release];
    [url release];
    [data release];
    [super dealloc];
}

- (void)requestFinished:(ASIHTTPRequest *)therequest {
    [delegate requestFinished:therequest];

    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];

    executing = NO;
    finished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

- (void)requestFailed:(ASIHTTPRequest *)therequest {
    [delegate requestFailed:therequest];
    [self willChangeValueForKey:@"isFinished"];
    [self willChangeValueForKey:@"isExecuting"];

    executing = NO;
    finished = YES;

    [self didChangeValueForKey:@"isExecuting"];
    [self didChangeValueForKey:@"isFinished"];
}

@end

The delegate of this PushContentRequest currently handles the interpretation of the ASIHTTPRequest's UserInfo Dictionary and the request in my implementation, though I suppose that it might make more sense to do this processing within the PushContentRequest's body.

Upvotes: 1

JosephH
JosephH

Reputation: 37495

I would do the following.

To start with, queue:

D, E, F and C

In the requestFinished delegate callback for D, E & F, check if the other all 3 requests have finished, if so send B.

Do the same for the callbacks for B & C - if they've both finished, send A.

You'd need some kind of object that's shared by all requests to store the results / status of earlier requests into.

Upvotes: 1

Related Questions