NSOperations之间的数据传输

时间:2014-06-14 19:28:58

标签: objective-c json nsoperation nsoperationqueue

我想获得以下内容:我在NSOperationQueue中有两个NSOperations。第一个是从网站下载(获取一些json数据),下一个是解析数据。这是相关的操作。 我不明白如何将它们联系在一起。如果它们都被分配并且在队列中,我如何将json字符串传递给解析它的操作?如果这个队列在另一个执行NSOperation的NSOperationQueue中,这个NSOperation包含前面提到的两个,那么这是一个问题吗?

我所能找到的只是将数据传输到主线程上的委托(performSelectorOnMainThread),但我需要所有这些操作在后台执行。

感谢。 码: NS下载:NSOperation

    - (instancetype)initWithURLString:(NSString *)urlString andDelegate:(id<JSONDataDelegate>)delegate
{
    self = [super init];
    if (self) {
        _urlStr = urlString;
        _delegate = delegate; /// this needs to be a NSOPeration
        _receivedData = [NSMutableData dataWithCapacity:256];
    }
    return self;
}

#pragma mark - OVERRIDE

    - (void)main
    {
        @autoreleasepool {

            if (self.isCancelled) {
                return;
            }

            NSURL *url = [NSURL URLWithString:self.urlStr];
            NSURLRequest *request = [NSURLRequest requestWithURL:url];
            self.urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
        }
    }

    #pragma mark - NSURLConnectionDataDelegate

    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
        if (self.isCancelled) {
            [connection cancel];
            self.receivedData = nil;
            return;
        }
        [self.receivedData appendData:data];
    }

    - (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
        if (self.isCancelled) {
            self.receivedData = nil;
            return;
        }
        // return data to the delegate
        NSDictionary *responseDict = @{JSON_REQUESTED_URL : self.urlStr,
                                       JSON_RECEIVED_RESPONSE : self.receivedData};
        [(NSObject *)self.delegate performSelectorOnMainThread:@selector(didReceiveJSONResponse:) withObject:responseDict waitUntilDone:NO]; // ok to uses performSelector as this data is not for use on the main thread ???
    }

    - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    {
        // return error to the delegate
        [(NSObject *)self.delegate performSelectorOnMainThread:@selector(didFailToReceiveDataWithError:) withObject:error waitUntilDone:NO];
    }

3 个答案:

答案 0 :(得分:1)

正如lucianomarisi指出的那样,通常最好让第一个操作产生第二个操作。这通常更易于管理。在我的经历中,操作依赖性并不常见。

尽管如此,它当然可以在操作之间传递数据。例如,您可以在第二个操作上创建datasource属性。那将是要求其数据的对象;该对象将是第一个操作。但是,这种方法可能需要锁定。

您还可以在第一个操作中创建nextOp属性。完成后,它会在退出之前调用setData:第二个操作。你可能不需要锁定,但你可能会。在大多数情况下,第一次操作最好只安排nextOp此时(这看起来像lucianomarisi的答案)。

关键是操作只是一个对象。它可以包含您想要的任何方法和属性。你可以将一个操作传递给另一个操作。

请记住,由于操作在后台运行,因此您无需使用NSURLConnection的异步接口。同步API(sendSynchronousRequest:returningResponse:error:对此很好,并且编码更简单。你甚至可以使用一个简单的NSBlockOperation。或者,你可以使用异步NSURLConnection接口,但是你真的不需要NSOperation

我也注意到了:

    _receivedData = [NSMutableData dataWithCapacity:256];

它真的是一小块JSON数据吗?很难相信这种复杂性对于将如此小的解析操作移到后台是值得的。

(作为旁注,除非您准确了解内存的大小,否则手动指定容量通常不会有太大好处。即便如此,它并不总是清楚它是什么?我相信NSURLConnection现在正在使用调度数据,所以你实际上要求一个永远不会被使用的内存块。当然Cocoa也不会分配它,因为它优化了...重点是你不妨使用[NSMutableData data]。可可对这些事情非常聪明;你通常只会妨碍它的优化。)

答案 1 :(得分:1)

@ user1028028:

使用以下方法。

1)维护用于添加DownloadOperation的操作队列引用。

2)在connectionDidFinishLoading方法中,创建ParseOperation实例,设置json数据并将其添加到操作队列。在DownloadOperation中维护ParseOperation强引用变量,并通过DownloadOperation接口处理取消解析操作。

3)完成解析后,在主线程中调用UI功能。

我希望这会有所帮助。

答案 2 :(得分:0)

正如Rob所说,除非您有任何特殊原因要使用操作,否则请使用同步调用。然后在MainThread或您需要的任何其他线程上执行选择器。除非您想在单独的操作或线程(显式)中分离检索和解析。

以下是我用于json检索和解析的代码:

-(BOOL) loadWithURL:(NSString*) url params: (NSDictionary*) params andOutElements:(NSDictionary*) jElements

{

NSError *reqError = nil;
NSString* urlStr = @"";//@"http://";
urlStr = [urlStr stringByAppendingString:url];

NSURL* nsURL = [NSURL URLWithString:urlStr];

//Private API to bypass certificate ERROR Use only for DEBUG
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[nsURL host]];


NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: nsURL
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:60.0];

[request setHTTPMethod:@"POST"];
NSString *postString = @"";
if(params!=nil) {
    NSEnumerator* enumerator = params.keyEnumerator;
    NSString* aKey = nil;
    while ( (aKey = [enumerator nextObject]) != nil) {
        NSString* value = [params objectForKey:aKey];

        //Use our own encoded implementation instead of above Apple one due to failing to encode '&'
        NSString* escapedUrlString =[value stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
        //Required to Fix Apple bug with not encoding the '&' to %26
        escapedUrlString =  [escapedUrlString stringByReplacingOccurrencesOfString: @"&" withString:@"%26"];

        //this is custom append method. Please implement it for you -> the result should be 'key=value' or '&keyNotFirst=value'
        postString = [self appendCGIPairs:postString key:aKey value:escapedUrlString isFirst:false];
    }
}


//************** Use custom enconding instead !!!! Error !!!!! **************
[request setHTTPBody:[postString dataUsingEncoding:NSUTF8StringEncoding]];


NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&reqError];
if(reqError!=nil) {
    NSLog(@"SP Error %@", reqError);
    return NO;
}

NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
//Handles Server Errors during execution of the web service that handles the call.
if([json_string hasPrefix:@"ERROR"] == YES){
    NSLog(@"SP Error %@", lastError);
    return NO;
}

//Very Careful!!!!!! Will stop for any reason.!!!!!!
//Handles errors from IIS Server that serves teh request.
NSRange range = [json_string rangeOfString:@"Runtime Error"];
if(range.location != NSNotFound) {
    NSLog(@"SP Error %@", lastError);
    return NO;
}

//Do the parsing
jElements = [[parser objectWithString:json_string error:nil] copy];
if([parser error] == nil) {
    NSLog(@"Parsing completed");
} else {
    jElements = nil;
    NSLog(@"Json Parser error: %@", parser.error);
    NSLog(@"Json string: %@", json_string);
    return NO;
}

//Parsed JSON will be on jElements
return YES;

}