我想获得以下内容:我在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];
}
答案 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;
}