2011-01-05 9 views
9

Estoy en el proceso de intentar mover el código de una clase UITableViewController a una clase "auxiliar".¿Cómo devolver un objeto de una clase que utiliza NSURLConnection y sus clases delegadas?

El código utiliza NSURLConnection para capturar y analizar JSON y luego llenar un NSMutableArray.

Lo que me gustaría hacer es llamar a un método en mi clase de ayuda que devuelve un NSMutableArray. Lo que no entiendo es cómo devolver la matriz de la clase delegada connectionDidFinishLoading de NSURLConnection (donde la matriz está realmente construida) como si fuera del método originalmente llamado que inició la conexión. En otras palabras, ¿cómo recupera el control el método que llama a NSURLConnection para que pueda devolver un valor de toda la operación?

Aquí están los métodos relevantes de la clase auxiliar. ¿Cómo obtengo el método getMovies para devolver el listOfMovies creado en la clase de delegado connectionDidFinishLoading?

-(NSMutableArray)getMovies:(NSURL*)url { 

responseData = [[NSMutableData data] retain];  

NSURLRequest *request = [NSURLRequest requestWithURL:url]; 
//NSURLRequest* request = [NSURLRequest requestWithURL: url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval: 30.0]; 

connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; 

} 

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { 

[responseData setLength:0]; 
} 

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

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
//TODO error handling for connection 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {  

//---initialize the array--- 

listOfMovies = [[NSMutableArray alloc] init]; 

tmdbMovies = [[NSArray alloc] init]; 
posters = [[NSArray alloc] init]; 
thumbs = [[NSDictionary alloc] init]; 

NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; 

SBJsonParser *json = [[SBJsonParser new] autorelease]; 

tmdbMovies = [json objectWithString:responseString]; 

// loop through all the top level elements in JSON 
for (id movie in tmdbMovies) { 

    // 0 - Name 
    // 1 - Meta 
    // 2 - Url 


    if ((NSNull *)[movie objectForKey:@"name"] != [NSNull null]) { 

     if (![[movie objectForKey:@"name"] isEqualToString:@""]) { 
      name = [movie objectForKey:@"name"]; 
     } 

    } 

    if ((NSNull *)[movie objectForKey:@"info"] != [NSNull null]) { 

     if (![[movie objectForKey:@"info"] isEqualToString:@""]) { 
      meta = [movie objectForKey:@"info"]; 
     } 

    } 

    if ((NSNull *)[movie objectForKey:@"thumb"] != [NSNull null]) { 

     if (![[movie objectForKey:@"thumb"] isEqualToString:@""]) { 
      thumbUrl = [movie objectForKey:@"thumb"]; 
     } 

    } 

    NSLog(@"Name: %@", name); 
    NSLog(@"Info: %@", meta); 
    NSLog(@"Thumb: %@", thumbUrl); 

    NSMutableArray *movieData = [[NSMutableArray alloc] initWithObjects:name,meta,thumbUrl,nil]; 

    // add movieData array to listOfJMovies array 
    [listOfMovies addObject:movieData]; 


    [movieData release]; 
} 


//FIXME: Connection warning 
if (connection!=nil) { 
    [connection release]; 
} 

[responseData release]; 
[responseString release]; 



} 
+1

De cualquier protocolo de uso o NSNotificationCenter – Vjy

Respuesta

13

Lo que realmente necesita hacer aquí es crear un @protocol que cree un delegado para su clase de ayuda. A continuación, cambie -(NSMutableArray)getMovies:(NSURL*)url a -(void)getMovies:(NSURL*)url

La clase que está llamando a su método de ayuda necesita implementar el delegado de su método auxiliar.

Luego, - (void)connectionDidFinishLoading:(NSURLConnection *)connection llama al (los) método (s) delegado (s). Lo mejor es tener uno para el éxito y otro para el fracaso.

= Actualizar Begin =

Usted necesitará también definir un id delegate en su archivo de ayuda que los conjuntos de la clase llamada a la libre después de init, pero antes de llamar -(void)getMovies:(NSURL*)url. De esa forma, el archivo auxiliar sabe a dónde devolver la llamada.

getMovies *movieListCall = [[getMovies alloc] init]; 
movieListCall.delegate = self; 
[movieListCall getMovies:<your NSURL goes here>]; 

verá algunas líneas adicionales para la inclusión de un delegate tanto en los archivos getMovies.h y getMovies.m.

= Actualizar Final =

en su archivo getMovies.h complemento:

@protocol getMoviesDelegate 

@required 
- (void)getMoviesSucceeded:(NSMutableArray *)movieArray; 
- (void)getMoviesFailed:(NSString *)failedMessage; 

@end 

@interface getMovies : NSOBject { 
id delegate; 
} 

@property (nonatomic, assign) id delegate; 

en su archivo getMovies.m complemento:

@synthesize delegate; 

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { 
    //TODO error handling for connection 
    if ([delegate respondsToSelector:@selector(getMoviesFailed:)]) { 
     [delegate getMoviesFailed:[error localizedDescription]]; 
    } 
} 

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {  

//finishes with 
    if ([delegate respondsToSelector:@selector(getMoviesSucceeded:)]) { 
     [delegate getMoviesSucceeded:listOfMovies]; 
    } 
} 

actualización del archivo .h la clase llamada a use getMoviesDelegate:

@interface MoviesView : UIViewController <getMoviesDelegate>{ 
. 
. 
. 
} 

añadir los métodos getMoviesDelegate a su archivo .m clase llamada

- (void)getMoviesSucceeded:(NSMutableArray *)movieArray { 
//deal with movieArray here 
} 

- (void)getMoviesFailed:(NSString *)failedMessage { 
//deal with failure here 
} 

Esto no se ha probado, pero espero que le da una hoja de ruta para trabajar.

Los protocolos son buenos porque puedes hacer tanto los métodos de delegado obligatorios como los opcionales y te ayudan a refinar tus métodos de ayuda para que sean muy reutilizables en todos los proyectos. El compilador también le advertirá si ha implementado un protocolo pero no ha implementado los métodos de delegado requeridos por el protocolo. Si sigue este camino sea seguro utilizar conformsToProtocol: y respondsToSelector:

+0

Esto parece un gran enfoque. Le dará una oportunidad e informará. Gracias a todos por su aporte. – user558096

+0

Recibo un error de delegado no declarado al llamar [delegar getMoviesSucceeded: listOfMovies]; Por cierto, ¿hay alguna manera de publicar el código en los comentarios? Me gustaría publicar mi archivo Movies.h para ver si he declarado los métodos incorrectamente. – user558096

+0

En realidad, el error ocurre en la condición if y es el siguiente: 'delegate' no declarado (primer uso en esta función) – user558096

0

Cómo me sale el método getMovies para devolver los listOfMovies que se construye en la clase connectionDidFinishLoading delegado?

Voy a argumentar que no deberías hacer eso.

Las solicitudes de red deben realizarse de forma asíncrona. Si su getMovies fuera a realizar una solicitud síncrona y solo regresara cuando tuviera datos, bloquearía ese hilo completo mientras espera que finalice una conexión de red. Esta es una mala idea en general y una idea terrible si su hilo principal es llamar al getMovies. Si bloquea el hilo principal, no podrá responder a los toques ni actualizar la interfaz de usuario, su aplicación aparecerá congelada y el sistema operativo la cancelará si sus usuarios no abandonan la tarea por frustración.

En lugar de eso, la clase auxiliar notifica a la persona que llama cuando hay datos disponibles (o cuando no pudo recuperar datos) a través de una llamada de delegado, notificaciones, KVO o el mecanismo que prefiera.

0

Estos son los pasos, pseudocódigo como el estilo:

  1. [helperInstance setDelegate:self]; // where self is your UITableViewController class

  2. en su clase de ayuda, en la connectionDidFinishLoading hacer algo como esto:

    [delegate finishedLoadingData:JSONData];

También puedes definir un protocolo para su delegado, y la declare el delegado como este en su clase de ayuda:

@property (nonatomic, assign) id<YourProtocol> delegate; 

Espero que esto ayude, Moszi

7

Fundamentalmente, lo que sucede es que estás empezando una carga de la red asíncrona (asíncrono es la forma correcta de hacerlo, casi con seguridad), y luego necesita alguna forma de reanudar cualquier operación que estuviera haciendo antes de que comenzara la carga.Usted tiene algunas opciones:

  • crear su propio protocolo delegado. Su UITableViewController se establecería entonces como el delegado del ayudante, y el ayudante llamaría al helperDidLoad o lo que sea que haya llamado ese método. Hay más información sobre cómo escribir delegados en la Guía de programación de Cocoa.

  • Usar bloques y estilo de paso de continuación. Esto es un poco más avanzado, pero me gusta. En su UITableViewController que iba a escribir algo como esto:

    [helper doSomething:^ (id loaded) { 
        [modelObject refresh:loaded]; // or whatever you need to do 
    }]; 
    

    Y luego, en su ayudante que iba a escribir:

    - (void)doSomething:(void^(id))continuation { 
        _continuation = continuation; 
        //kick off network load 
    } 
    
    
    - (void)connectionDidFinishLoading:(NSURLConnection *)connection { 
        _continuation(_data); 
    } 
    
  • Use notificaciones. Lea los documentos NSNotificationCenter.
  • Utilice KVO. La guía de programación de KVO tiene mucha información útil sobre Observación de valores clave.
+0

Hola, sé que esto es un post muy antiguo, pero, en un caso en el que hay más de una conexión comenzado casi al mismo tiempo, ¿cómo determinamos qué resultado proviene de qué conexión? Gracias. – tyegah123

Cuestiones relacionadas