2012-05-31 2 views
4

J'ai la classe suivante qui fait une demande de poste HTTP de manière asynchrone pour éviter des problèmes sur le thread principal de l'interface utilisateur:Comment revenez-vous d'un NSURLConnection asynchrone à la classe appelante?

@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 

J'invoque ce qui précède de mon ViewController en utilisant la ligne de code suivante:

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

Alors, voilà comment je comprends:

  1. Le doLookup exécutera la demande sur une connexion asynchrone .
  2. Lorsque les données ont été complètement chargé, il appellera connectionDidFinishLoading
  3. Une fois les données a terminé le chargement, je bâtirai un objet à partir des données de réponse que je vais renvoyer au contrôleur d'appel

Comment puis-je écouter le contrôleur d'appel pour cela? Ai-je besoin d'implémenter mes propres méthodes de rappel dans le ViewController pour écouter l'invocation, puis arrêter un spinner et mettre à jour l'interface utilisateur en fonction du contenu de myObj?

J'espère Theres une façon vraiment facile que j'ai négligé ...

Merci

Répondre

6

Ouais, vous devez implémenter votre rappel à l'aide du modèle de délégué. C'est, à mon avis, la façon la plus simple et la plus standard de le faire. Il y a d'autres façons, comme vous pouvez le voir dans les autres réponses.

Dans votre fichier DataFeeder.h:

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

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

Dans votre 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 

Vous instancierez un objet DataFeeder comme ceci:

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

Bien sûr, le contrôleur de vue d'appel doit implémenter les méthodes DataFeederDelegate.

+0

Comment puis-je définir le 'delegate_', et plus important encore, où dois-je le définir? Vraisemblablement dans 'ViewController' quand je crée et initialise le' DataFeeder' avant l'utilisation? – Jimmy

+0

Edited ma réponse, vérifiez-le. Rien d'autre, demandez. –

+0

Parfait, ça m'a aidé! Pas tout à fait aussi élégant que les tâches Async dans Android mais bon ho. – Jimmy

4

Vous avez plusieurs approches pour obtenir vous ViewController averti lorsque les données sont là:

  1. définir un protocole de délégué entre le ViewController et le DataFeeder, afin que ce dernier envoie un message à l'ancien dans connectionDidFinishLoading:;

  2. utilisation NSNotificationCenter ainsi découpler DataFeeder et ViewController: ViewController s'ajoute comme observateur au centre de notification par défaut:

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

en DataFeeder envoyer la notification au bon moment:

 [[NSNotificationCenter defaultCenter] postNotificationName:kMyNetworkNotificationDataIsThere object:self]; 
  1. make ViewController implémenter les méthodes de délégué pour NSURLConnection et gérer la réponse elle-même (ceci nécessitera de le passer en paramètre au constructeur DataFeeder).
2

Une bonne façon de le faire serait d'utiliser des blocs. Votre méthode doLookup: peut accepter un objet bloc et vous pouvez l'appeler lorsque la connexion se termine.

Étant donné que vous souhaitez probablement effectuer plusieurs recherches avec des blocs d'achèvement différents, vous devez associer le bloc transmis avec la connexion NSURLConnection appropriée.

Pour ce faire, ous pouvez soit utiliser une sous-classe NSURLConnection avec une propriété completionBlock, ou utiliser des objets associés C-objectif (en utilisant les fonctions objc_setAssociatedObject et objc_getAssociatedObject) pour fixer le bloc à l'objet de connexion. Lorsque tout est prêt dans la méthode connectionDidFinishLoading: et que vous avez préparé l'objet de réponse final, saisissez le bloc de l'objet NSURLConnection et appelez-le, en lui transmettant les données finales.

Vous voulez éventuellement votre code client pour ressembler à ceci:

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

    // access the returned data 
}]; 

J'espère que cela était suffisamment de détails pour vous.

+0

+1 pour la réponse, mais malheureusement, je suis relativement nouveau à iOS donc opté pour l'option (un peu plus facile à comprendre) délégué/protocole. Merci – Jimmy

Questions connexes