2012-05-31 2 views
4

У меня есть следующий класс, который делает запрос HTTP POST асинхронно, чтобы избежать проблем на основной поток пользовательского интерфейса:Как вы возвращаетесь из асинхронного NSURLConnection к вызывающему классу?

@implementation DataFeeder 

-(void) doLookup:(NSString *)inputValue 
{ 
    NSString *myRequestString = [NSString stringWithFormat:@"val=%@", inputValue]; 
    NSMutableData *myRequestData = [ NSMutableData dataWithBytes: [ myRequestString UTF8String ] length: [ myRequestString length ] ]; 

    NSURL * myUrl = [NSURL URLWithString: @"http://mywebsite/results.php"]; 
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myUrl]; 
    [request setHTTPMethod: @"POST"]; 
    [request setHTTPBody: myRequestData]; 
    [request setTimeoutInterval:10.0]; 

    [[NSURLConnection alloc] initWithRequest:request delegate:self]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 
    responseData = [[NSMutableData alloc] init]; 
} 

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
    [responseData appendData:data]; 
} 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
    // Show error message 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
    // Use responseData 
    // Got all my response data here, so build up an object ready to send back 
} 

@end 

Я вызова выше из моих ViewController, используя следующую строку кода:

MyObject * myObj = [feeder doLookup:@"SomeStaticStringForNow"];

Итак, это, как я понимаю:

  1. doLookup выполнит запрос по асинхронному соединению .
  2. Когда данные были полностью загружены, он будет вызывать connectionDidFinishLoading
  3. После того, как данные по окончанию загрузки, я буду строить объект из данных ответов, которые я пошлю назад к контроллеру вызывающей

Как я могу заставить вызывающий контроллер слушать это? Нужно ли мне реализовать свои собственные методы обратного вызова в ViewController, которые будут прослушивать для вызова, а затем остановить счетчик и обновить пользовательский интерфейс на основе содержимого myObj?

Я надеюсь, что Theres действительно простой способ, что я упускать из виду ...

Спасибо

ответ

6

Да, вы должны реализовать функцию обратного вызова, используя шаблон делегата. То есть, на мой взгляд, самый простой и стандартный способ сделать это. Есть и другие способы, как вы можете видеть в других ответах.

В вашем DataFeeder.h файле:

@protocol DataFeederDelegate 
- (void)dataReady:(NSData*)data; 
@end 

@interface DataFeeder : NSObject { 
id delegate_; 
} 
- (id)initWithDelegate:(id<DataFeederDelegate>)delegate; 
@end 

В вашем DataFeeder.m:

@implementation DataFeeder 
- (id)initWithDelegate:(id<DataFeederDelegate>)delegate { 
    self = [super init]; 
    if(self) { 
    delegate_ = delegate; 
    } 
    return self; 
} 
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
[delegate_ dataReady:responseData]; 
} 
@end 

Вы бы создать экземпляр объекта DataFeeder так:

DataFeeder *dataFeeder = [[DataFeeder alloc] initWithDelegate:self]; 

Конечно, контроллер вида вызова должен реализовать методы DataFeederDelegate.

+0

Как установить 'делегат_', и что более важно, где его установить? Предположительно, в «ViewController», когда я создавал и инициализировал «DataFeeder» перед использованием? – Jimmy

+0

Отредактировал свой ответ, проверьте его. Что-нибудь еще, просто спроси. –

+0

Отлично, это помогло мне! Не так элегантно, как задачи Async в android, но hey ho. – Jimmy

4

У вас есть несколько подходов к получать вы ViewController уведомление, когда данные есть:

  1. определяет протокол делегата между ViewController и DataFeeder, так что последний посылает сообщение бывший в connectionDidFinishLoading:;

  2. использование NSNotificationCenter так разъединить DataFeeder и ViewController: ViewController добавляет себя в качестве наблюдателя от центра уведомлений по умолчанию:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataIsThere:) name:kMyNetworkNotificationDataIsThere object:nil]; 
    

пока DataFeeder отправить уведомление в нужное время:

 [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self]; 
  1. make ViewController реализовать методы делегата для NSURLConnection и обрабатывать сам ответ (для этого потребуется передать его как параметр в конструктор DataFeeder).
2

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

Поскольку вы, вероятно, хотите иметь возможность выполнять несколько поисков с различными блоками завершения, вам необходимо связать переданный блок с соответствующим NSURLConnection.

Для этого НУ может либо использовать подкласс NSURLConnection с completionBlock собственности, или использовать Objective-C, связанные объекты (с помощью objc_setAssociatedObject и objc_getAssociatedObject функции), чтобы присоединить блок к объекту соединения.

Когда все готово в методе connectionDidFinishLoading:, и вы подготовили окончательный объект ответа, вы захватите блок из объекта NSURLConnection и вызовите его, передав ему окончательные данные.

Таким образом, вы в конечном итоге хотите, чтобы ваш код клиента, чтобы выглядеть следующим образом:

[feeder doLookup:@"Something" completionBlock:(FetchedData *data, NSError *error) { 
    if (error) { 
     // ... 
     return; 
    } 

    // access the returned data 
}]; 

Я надеюсь, что это было достаточно подробно для вас.

+0

+1 для ответа, но, к сожалению, я относительно новичок в iOS, поэтому решил (немного легче понять) вариант делегирования/протокола. благодаря – Jimmy

Смежные вопросы