Прокси-сервер NSURLConnection NSURLRequest для асинхронных вызовов веб-службы

У меня есть несколько представлений, которые делают одно и то же NSURLRequest/NSURLConnection request. В идеале, чтобы получить повторное использование кода, я хотел бы иметь какой-то «прокси», который выполняет всю основную работу по созданию/выполнению (асинхронного) запроса/соединения, настройке всех методов делегата и т. д. , поэтому мне не нужно копировать все эти NSURLConnection обработчики методов делегата в каждом представлении. Прежде всего, разумен ли такой подход к проектированию? Во-вторых, как я могу сделать что-то подобное?

Для небольшой справочной информации я попытался это сделать и заставил его «работать», однако, похоже, он не выполняется асинхронно. Я создал файл Proxy.h/m, в котором есть методы экземпляра для различных вызовов веб-сервиса (а также методы NSURLConnection делегата):

@interface Proxy : NSObject {

    NSMutableData *responseData;
    id<WSResponseProtocol> delegate;
}

- (void)searchForSomethingAsync:(NSString *)searchString delegate:(id<WSResponseProtocol>)delegateObj;

@property (nonatomic, retain) NSMutableData *responseData;
@property (assign) id<WSResponseProtocol> delegate;

@end

WSResponseProtocol определяется следующим образом:

@protocol WSResponseProtocol <NSObject>

@optional
- (void)responseData:(NSData *)data;
- (void)didFailWithError:(NSError *)error;

@end

Чтобы использовать это, контроллер представления просто должен соответствовать протоколу WSResponseProtocol, чтобы перехватывать ответы. Вызов веб-службы выполняется следующим образом:

Proxy *p = [[Proxy alloc] init];
[p searchForSomethingAsync:searchText delegate:self];
[p release];

Я могу предоставить больше кода, но остальное можно предположить. Перед вызовом я «начинаю анимацию» UIActivityIndicatorView счетчика. Но спиннер никогда не крутится. Если я просто поставлю методы делегата NSURLConnection прямо в контроллер представления, то спиннер крутится. Итак, это заставляет меня думать, что моя реализация не выполняется асинхронно. Любые мысли/идеи здесь?


person tbehunin    schedule 24.12.2009    source источник


Ответы (2)


Ваш подход разумен, однако я не уверен, почему вы создаете свой собственный протокол. В этом нет необходимости. Все, что вам нужно для реализации этого, находится в документации Apple. на NSURLConnection. Если вы возьмете код с той страницы, где создается экземпляр NSURLConnection, и сделаете соединение ivar вместо того, чтобы просто создать его как локальную переменную, вы сможете сравнить объекты соединения в каждом из методов обратного вызова и ответить соответствующим образом. Например, возьмите этот код из документации и измените объект соединения на ivar:

// create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.apple.com/"]
                        cachePolicy:NSURLRequestUseProtocolCachePolicy
                    timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
    // Create the NSMutableData that will hold
    // the received data
    // receivedData is declared as a method instance elsewhere
    receivedData=[[NSMutableData data] retain];
} else {
    // inform the user that the download could not be made
}

Переменная theConnection — это наша переменная. Затем вы можете проверить это следующим образом:

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    if (connection == theConnection)
    {
        // do something with the data object.
        [connectionSpecificDataObject appendData:data];
    }
}

Вы, конечно, можете реализовать его, создав свой собственный протокол, как вы предлагаете, а затем перезвонить делегату, который соответствует вашему протоколу, но вам может быть лучше просто создать экземпляр вашего объекта, используя селектор успеха и неудачи, который вы можете проверить. Что-то вроде этого:

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    if (connection == theConnection)
    {
        if (delegate && [delegate respondsToSelector:successSelector])
            [delegate performSelector:successSelector 
                           withObject:connectionSpecificDataObject];
    }
    [connection release];
}

Где dataDidDownloadSelector — это переменная экземпляра SEL, которую вы установили при создании своего делегата загрузки, в котором содержится весь этот код — ваш объект Proxy. Что-то вроде этого:

Proxy *p = [[Proxy alloc] init];
[p searchForSomethingAsync:searchText 
                  delegate:self 
           successSelector:@selector(didFinishWithData:) 
              failSelector:@selector(didFailWithError:)];

Реализуйте свои селекторы следующим образом:

- (void)didFinishWithData:(NSData*)data;
{
    // Do something with data
}

- (void)didFailWithError:(NSError*)error
{
    // Do something with error
}

Это стало более длинным ответом, чем я предполагал. Дайте мне знать, если это не имеет смысла, и я могу попытаться уточнить.

С наилучшими пожеланиями,

person Matt Long    schedule 24.12.2009
comment
Ах - очень хорошо! Я понимаю, что вы говорите - ваше решение устранит необходимость в протоколе. Гораздо проще! Спасибо за это! Тем не менее, я думаю, что начинаю разогреваться перед решением, предложенным Кендалом, то есть иметь синхронные NSURLConnections, обернутые в NSOperation и поставленные в очередь. Это решение может быть более простым решением проблемы создания структуры асинхронных вызовов веб-служб, хотя мой подход + ваше понимание все еще жизнеспособны. - person tbehunin; 29.12.2009
comment
Возвращаю свой предыдущий комментарий. Этот подход я использую в сравнении с синхронными NSURLConnections в NSOperation. NSURLConnection уже обеспечивает необходимую мне многопоточность, не помещая ее в NSOperation. Кроме того, прочитав больше блогов, синхронная NSURLConnections + аутентификация не предоставляет столько ловушек, сколько асинхронные вызовы. - person tbehunin; 01.01.2010
comment
Где\как объявляются данные. В документации просто говорится, что он объявлен в другом месте, но не дается информации о том, как это сделать. - person Marcel Marino; 03.10.2012
comment
Это свойство или переменная класса, в котором объявлен этот код. Он должен быть доступен через [self receivedData] - person Matt Long; 04.10.2012

Ваш код как есть, я думаю, ничего не сделает - вы выпускаете прокси сразу после инициализации, поэтому он даже не запускается.

Подход, который мне нравится использовать, — это синхронные вызовы NSURLConnection внутри NSOperation, которым, в свою очередь, управляет NSOperationQueue. Я делаю объект очередью, живущей в синглтоне, поэтому я просто получаю доступ к экземпляру из любого места и сообщаю ему, когда мне нужно начать новое соединение.

person Kendall Helmstetter Gelner    schedule 24.12.2009
comment
Хороший! Я никогда не думал об этом подходе синхронных вызовов NSURLConnection, обернутых в NSOperation. Я думаю, что это будет решение, которое я ищу. Однако с этим решением вам нужен способ получения информации. Вот ссылка на другой ответ SO на эту проблему: ком/вопросы/1297733/ - person tbehunin; 29.12.2009
comment
Чтобы передать результаты, я обычно сохраняю результат данных где-то в центральном месте с помощью синглтона и/или возвращаю объект всем заинтересованным, завернутый в уведомление (вы можете обернуть любой объект в словарь userInfo уведомлений). - person Kendall Helmstetter Gelner; 30.12.2009