I am writing a web-connected application that 开发者_StackOverflowneeds 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
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.
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.
精彩评论