NSURLConnection NSURLRequest прокси для асинхронных вызовов веб-сервисов - PullRequest
5 голосов
/ 24 декабря 2009

У меня есть несколько просмотров, которые делают то же самое 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 непосредственно в контроллер представления, тогда спиннер вращается. Таким образом, это заставляет меня думать, что моя реализация не выполняется асинхронно. Любые мысли / идеи здесь?

Ответы [ 2 ]

22 голосов
/ 24 декабря 2009

Ваш подход разумен, однако я не уверен, почему вы создаете свой собственный протокол. Это не обязательно. Все, что вам нужно для реализации этого, находится в документации 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 - это наш ivar. Тогда вы можете проверить это так:

- (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
}

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

С уважением,

1 голос
/ 25 декабря 2009

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

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

...