带有NSOperationQueue的iOS 5 NSURLConnection - 提供UI反馈

时间:2012-03-11 17:38:46

标签: ios5 nsurlconnection nsoperationqueue

我需要为JSON Web服务创建多个NSURLConnections。我希望每个WS调用都保持在UI中,可能带有UIActivityIndi​​catorView和标签。到目前为止,我已经创建了一个NSURLConnection帮助程序类来处理连接并将URL委托放在View中。这适用于使用单个WS调用更新UI。

对于多个来电,我正在尝试使用NSOperationQueue。我想将setMaxConcurrentOperationCount设置为队列中的一个,以便每个Operation一次执行一个。这是我的View Controller上的相关代码:

ViewController.m

#import "URLOperationHelper.h"

@implementation ViewController

- (IBAction)showPopup:(id)sender 
{
    // Dictonary holds POST values
    NSMutableDictionary *reqDic = [NSMutableDictionary dictionary];

    // Populate POST key/value pairs
    [reqDic setObject:@"pw" forKey:@"Password"];
    [reqDic setObject:@"ur" forKey:@"UserName"];

    operationQueue = [[NSOperationQueue alloc] init];
    [operationQueue setMaxConcurrentOperationCount:1];
    [operationQueue cancelAllOperations];
    [operationQueue setSuspended:YES];    

    URLOperationHelper *wsCall1 =  [[URLOperationHelper alloc] initWithURL:@"urlString1" postParameters:reqDic urlDelegate:self];

    URLOperationHelper *wsCall2 =  [[URLOperationHelper alloc] initWithURL:@"urlString2" postParameters:reqDic urlDelegate:self];

    [operationQueue addOperation:wsCall1];
    [operationQueue addOperation:wsCall2];        

}

// Did the URL Connection receive a response
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    NSLog(@"Did receive response: %@", response);

    NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
    int code = [httpResponse statusCode];

    // Handle status code here

    webData = [[NSMutableData alloc]init];
}

// Did the URL Connection receive data
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    NSLog(@"Did receive data: %@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);

    assert(webData != nil);
    [webData appendData:data];
}

// Did the connection fail with an error?
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"%@", error);
}

// Executes after a successful connection and data download
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"Connection finished");
} 

@end 

这是我的 URLOperationHelper.m

  @implementation URLHelper  
    - (id)initWithURL:(NSString *)urlPath
       postParameters:(NSMutableDictionary *)postParameters
    urlParentDelegate:(id) pDelegate
    {
        if(self = [super init])
        {
            connectionURL = urlPath;
            postParams = postParameters;
            parentDelegate = pDelegate;
        }

        return self;
    }

    - (void)done
    {
        // Cancel the connection if present
        if(urlConnection)
        {
            [urlConnection cancel];
            urlConnection = nil;
        }

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

        executing = NO;
        finished = YES;

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

    - (void)cancel
    {
        // Possibly add an NSError Property
        [self done];
    }

    - (void)start
    {
        // Make sure this operation starts on the main thread
        if(![NSThread isMainThread])
        {
            [self performSelectorOnMainThread:@selector(start) withObject:nil waitUntilDone:NO];
            return;
        }

        // Make sure that the operation executes
        if(finished || [self isCancelled])
        {
            [self done];
            return;
        }

        [self willChangeValueForKey:@"isExecuting"];
        executing = YES;

        [self main];
        [self willChangeValueForKey:@"isExecuting"];
    }

    - (void)main
    {
        NSError *error = nil;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:postParams options:NSJSONWritingPrettyPrinted error:&error];

        // Convert dictionary to JSON  
        NSString *requestJSON = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];

        NSLog(@"JSONRequest: %@", requestJSON);

        // Declare Webservice URL, request, and return data
        url = [[NSURL alloc] initWithString:connectionURL];
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
        NSData *requestData = [NSData dataWithBytes:[requestJSON UTF8String] length:[requestJSON length]];

        // Build the request
        [request setHTTPMethod:@"POST"];
        [request setValue:[NSString stringWithFormat:@"%d", [requestData length]] forHTTPHeaderField:@"Content-Length"];
        [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        [request setHTTPBody:requestData];

        // Connect to Webservice
        // Responses are handled in the delegates below
        urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:parentDelegate startImmediately:YES]; 
    }

    - (BOOL)isConcurrent
    {
        return YES;
    }

    - (BOOL)isExecuting
    {
        return executing;
    }

    -(BOOL)isFinished
    {
        return finished;
    }

    @end

我遇到的问题是从不调用URLOperation的Start方法。创建OperationQueue并调用操作,但在此之后,执行或线程方式都没有发生。

此外,使用像这样的NSOperationQueues提供UI反馈是否正确? I.E.从Operation?

调用NSURLDelegates

1 个答案:

答案 0 :(得分:3)

如果在添加操作之前将setSuspended设置为YES,那么您的操作将排队到暂停的队列中。我建议不要暂停队列

此外,您的操作无论如何都不会结束。您需要将操作本身指定为委托并实现所有必需的委托方法。在这些方法中,您可以将邮件转发到parentDelegate并确定完成时间并在适当时调用您的完成方法(我建议connection:didFailWithError:connectionDidFinishLoading:

这里有一个很好的教程:http://blog.9mmedia.com/?p=549

您也没有完全实现正确的键值编码兼容属性。每当您致电willChangeValueForKey:时,您还需要事后致电didChangeValueForKey

- (void)start
{
    ...
    [self willChangeValueForKey:@"isExecuting"];
    executing = YES;
    [self didChangeValueForKey:@"isExecuting"];

    [self main];
}

- (void)done
{
    ...

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

    executing = NO;
    finished = YES;

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

请参阅此Q / A for KVC:when to use "willChangeValueForKey" and "didChangeValueForKey"?