Passing Data Between Two Nsoperations

Passing Data Between Two NSOperations

Your issue is that you are constructing your ParseJSONOperation with a nil value. Since you have two operations that rely on this NSData object I would suggest you write a wrapper object to house this data.

To try and be aligned with the WWDC talk lets call this object the APIResultContext:

class APIResultContext {
var data: NSData?
}

now we can pass this object into both the APIQueryOperation and the ParseJSONOperation so that we have a valid object that can store the data transferred from the API.

This would make the constructors for the query:

let context = APIResultContext()
APIQueryOperation(request: registerAPICall, context: context)
ParseJSONOperation(context: context)

Inside your ParseJSONOperation you should be able to access the data assuming the query completes after it sets the data.

Thread Safety

As @CouchDeveloper pointed out, data is not strictly speaking thread safe. For this trivial example since the two operations are dependent we can safely write and read knowing that these accesses wont take place at the same time. However, to round the solution up and make the context thread safe we can add a simple NSLock

class APIResultContext {
var data: NSData? {
set {
lock.lock()
_data = newValue
lock.unlock()
}
get {
lock.lock()
var result = _data
lock.unlock()
return result
}
}

private var _data: NSData?
private let lock = NSLock()
}

Data transfer between NSOperations

@user1028028:

Use the following approach.

1) Maintain the operation queue reference that you are using to add DownloadOperation.

2) In connectionDidFinishLoading method, create ParseOperation instance, set the json data and add it to operation queue. Maintain ParseOperation strong reference variable in DownloadOperation and handling of cancelling of parsing operation through DownloadOperation interface.

3) After completed parsing call the UI functionality in main thread.

I hope this helps.

How to communicate results between NSOperation dependencies?

I remember when NSOperation was first introduced, and I had to write a introductory article for the ADC site that would first download some photos, and then render them into a poster. I ran into similar issues there: using dependencies to control order, but then finding I had to pass the image filenames to the dependent operation.

That was many years ago, and at that time I had NSOperation subclasses for each task. I set dependencies between them, and added a delegate to the operations that needed to be passed results from an earlier operation. In the delegate method, the controller object would retrieve the results from a property of the first operation, and set them via a property on the second.

Perhaps a better solution is to have the relationships between operations explicit, not only in terms of dependencies, but also in terms of the passing of data. So you could create NSOperation subclasses which pass data to the next NSOperation as part of standard operation, or — more elegantly — pull data from a completed operation.

To make this more concrete: operation B depends on A being complete. A generates resource R which is needed by B to run. You add a property to B that references the A object. When B runs, it simply retrieves R from the A object.

If you would rather not create operation subclasses, and just want to avoid nesting of blocks, you could consider using a queueing mechanism that gives you a bit more control, such as CDEAsynchronousTaskQueue.

NSOperation pass back data

Another idea would be to use blocks. You can initialize your NSOperation object with a block

typedef void(^CompletionBlock)(NSData *data);

- (id) initWithCompletionBlock: (CompletionBlock) block;

and call it after the operation is finished and data has been downloaded.

dispatch_async(dispatch_get_main_queue(), ^{
if(block){
block(fetchedData);
}
});

Chaining `NSOperation` : Pass result from an operation to the next one

If you want to chain operations, but don't like the nesting, you can use NSOperation subclasses, and then define your own completion handlers:

DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url];
ParseOperation *parseOperation = [[ParseOperation alloc] init];
DownloadImagesOperation *downloadImagesOperation = [[DownloadImagesOperation alloc] init];

downloadOperation.downloadCompletionHandler = ^(NSData *data, NSError *error) {
if (error != nil) {
NSLog(@"%@", error);
return;
}

parseOperation.data = data;
[queue addOperation:parseOperation];
};

parseOperation.parseCompletionHandler = ^(NSDictionary *dictionary, NSError *error) {
if (error != nil) {
NSLog(@"%@", error);
return;
}

NSArray *images = ...;

downloadImagesOperation.images = images;
[queue addOperation:downloadImagesOperation];
};

[queue addOperation:downloadOperation];

Frankly, though, I'm not sure that's any more intuitive than the nested approach:

DownloadOperation *downloadOperation = [[DownloadOperation alloc] initWithURL:url downloadCompletionHandler:^(NSData *data, NSError *error) {
if (error != nil) {
NSLog(@"%@", error);
return;
}

ParseOperation *parseOperation = [[ParseOperation alloc] initWithURL:data parseCompletionHandler:^(NSDictionary *dictionary, NSError *error) {
if (error != nil) {
NSLog(@"%@", error);
return;
}

NSArray *images = ...

DownloadImagesOperation *downloadImagesOperation = [[DownloadImagesOperation alloc] initWithImages:images imageDownloadCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(@"%@", error);
return;
}

// everything OK
}];
[queue addOperation:downloadImagesOperation];
}];
[queue addOperation:parseOperation];
}];
[queue addOperation:downloadOperation];

By the way, the above assumes that you're familiar with subclassing NSOperation, especially the subtleties of creating an asynchronous NSOperation subclass (and doing all of the necessary KVO). If you need examples of how that's done, let me know.

Return data from NSOperation?

Sure. Declare a delegate in NSOperation subclass. Then after finish of operation

if([self.delegate respondsToSelector:@selector(YourDelegate:)]) {
[(NSObject *)self.delegate performSelectorOnMainThread:@selector(YourDelegate:) withObject:self waitUntilDone:NO];
}

In UI

-(void)YourOperationDidFinish:(YourOperation *)downloader {
if(downloader.downloadItem) {
// processing with your object
}
}


Related Topics



Leave a reply



Submit