iOS 5 NSURLConnection с NSOperationQueue - Предоставление обратной связи с пользовательским интерфейсом - PullRequest
2 голосов
/ 11 марта 2012

Мне нужно сделать несколько соединений NSURLC с веб-службой JSON.Я хотел бы, чтобы каждый вызов WS содержался в пользовательском интерфейсе, возможно, с UIActivityIndicatorView и меткой.До сих пор я создал вспомогательный класс NSURLConnection для обработки соединения и разместил делегаты URL в представлении.Это прекрасно работает для обновления интерфейса с помощью одного вызова WS.

Для нескольких вызовов я пытаюсь использовать NSOperationQueue.Я бы хотел установить MaxConcurrentOperationCount в единицу в очереди, чтобы каждая операция выполнялась по одной за раз.Вот соответствующий код на моем 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

Проблема, с которой я столкнулся, заключается в том, что метод Start для URLOperation никогда не вызывается.Создается OperationQueue и вызываются Операции, но после этого ничего не происходит, выполнение или поток.

Кроме того, является ли это правильным подходом к предоставлению обратной связи с пользовательским интерфейсом, используя подобные NSOperationQueues?IE вызывает NSURLDelegates из операции?

1 Ответ

3 голосов
/ 11 марта 2012

Если вы установите 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 для KVC: когда использовать "willChangeValueForKey" и "didChangeValueForKey"?

...