2011-03-14 7 views

Respuesta

37

El problema es que performSelectorInBackground:withObject: toma sólo un argumento objeto. Una forma de superar esta limitación es pasar un diccionario (o matriz) de argumentos a un método de "contenedor" que deconstruye los argumentos y llama a su método actual:

- (void)callingMethod { 
    NSDictionary * args = [NSDictionary dictionaryWithObjectsAndKeys: 
          [NSNumber numberWithInteger:pageIndex], @"pageIndex", 
          [NSNumber numberWithBool:firstCase], @"firstCase", 
          nil]; 
    [self performSelectorInBackground:@selector(reloadPageWrapper:) 
          withObject:args]; 
} 

- (void)reloadPageWrapper:(NSDictionary *)args { 
    [self reloadPage:[[args objectForKey:@"pageIndex"] integerValue] 
      firstCase:[[args objectForKey:@"firstCase"] boolValue]]; 
} 

- (void)reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase { 
    // Your code here... 
}

De esta manera sólo estás pasando un " único "argumento para la llamada de fondo, pero ese método puede construir los múltiples argumentos que necesita para la llamada real (que tendrá lugar en el mismo hilo de fondo).

+1

Mediante el uso de la lista de clase NSInvocation y argumentos variables se podría escribir una clase de envoltura genérica muy elegante ... ¡Tal vez la próxima vez que necesite escribir otro envoltorio lo pruebe! – hariseldon78

+0

@ hariseldon78 +1 me inspiró para escribir tal cosa, mira mi respuesta. –

+1

En estos días realmente debería usar dispatch_async (...) para hacer algo como esto, en lugar de -performSelectorInBackground: ... –

5

Bueno, he utilizado este:

[self performSelectorInBackground:@selector(reloadPage:) 
         withObject:[NSArray arrayWithObjects:pageIndex,firstCase,nil] ]; 

para esto:

- (void) reloadPage: (NSArray *) args { 
    NSString *pageIndex = [args objectAtIndex:0];  
    NSString *firstCase = [args objectAtIndex:1];  
} 
+3

Esto tendrá un 'NSArray'. Debería usar '[NSArray arrayWithObjects: ...]' en lugar de '[[NSArray alloc] initWithObjects: ...]'. – respectTheCode

+0

Gracias respetoTheCode, soy flojo, así que siempre uso autorelease.Gracias de todos modos. –

+0

Esto no parece funcionar a menos que el selector que estoy realizando esté esperando una matriz. De lo contrario, el primer parámetro en el selector debería ser la matriz. – Will

6

Acabo de encontrar esta pregunta y no estaba contento con cualquiera de las respuestas. En mi opinión, ni hacer un buen uso de las herramientas disponibles, y pasar información arbitraria en matrices y diccionarios generalmente me preocupa.

Así que fui y escribí una pequeña NSObject categoría que invocará un selector arbitraria con un número variable de argumentos:

Cabecera de Categoría

@interface NSObject (NxAdditions) 

-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION; 

@end 

Categoría Implementación

@implementation NSObject (NxAdditions) 

-(void)performSelectorInBackground:(SEL)selector withObjects:(id)object, ... 
{ 
    NSMethodSignature *signature = [self methodSignatureForSelector:selector]; 

    // Setup the invocation 
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; 
    invocation.target = self; 
    invocation.selector = selector; 

    // Associate the arguments 
    va_list objects; 
    va_start(objects, object); 
    unsigned int objectCounter = 2; 
    for (id obj = object; obj != nil; obj = va_arg(objects, id)) 
    { 
     [invocation setArgument:&obj atIndex:objectCounter++]; 
    } 
    va_end(objects); 

    // Make sure to invoke on a background queue 
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithInvocation:invocation]; 
    NSOperationQueue *backgroundQueue = [[NSOperationQueue alloc] init]; 
    [backgroundQueue addOperation:operation]; 
} 

@end 

Uso

-(void)backgroundMethodWithAString:(NSString *)someString array:(NSArray *)array andDictionary:(NSDictionary *)dict 
{ 
    NSLog(@"String: %@", someString); 
    NSLog(@"Array: %@", array); 
    NSLog(@"Dict: %@", dict); 
} 

-(void)someOtherMethod 
{ 
    NSString *str = @"Hello world"; 
    NSArray *arr = @[@(1337), @(42)]; 
    NSDictionary *dict = @{@"site" : @"Stack Overflow", 
          @"url" : [NSURL URLWithString:@"http://stackoverflow.com"]}; 

    [self performSelectorInBackground:@selector(backgroundMethodWithAString:array:andDictionary:) 
          withObjects:str, arr, dict, nil]; 
} 
+0

Esta es la solución más elegante al problema. –

0

con performSelectorInBackground sólo se puede pasar un argumento, así que un objeto personalizado para este método para almacenar los datos, itll ser más conciso que un diccionario ambigua o matriz. El beneficio de esto es que puede pasar el mismo objeto cuando está hecho y contiene varias propiedades de devolución.

#import <Foundation/Foundation.h> 

@interface ObjectToPassToMethod : NSObject 

@property (nonatomic, strong) NSString *inputValue1; 
@property (nonatomic, strong) NSArray *inputArray; 
@property (nonatomic) NSInteger returnValue1; 
@property (nonatomic) NSInteger returnValue2; 

@end 

y pasar ese objeto a su método:

ObjectToPassToMethod *obj = [[ObjectToPassToMethod alloc] init]; 
obj.inputArray = @[]; 
obj.inputValue1 = @"value"; 
[self performSelectorInBackground:@selector(backgroundMethod:) withObject:obj]; 


-(void)backgroundMethod:(ObjectToPassToMethod*)obj 
{ 
    obj.returnValue1 = 3; 
    obj.returnValue2 = 90; 
} 

asegúrese de limpiar el objeto cuando se hace para evitar pérdidas de memoria

Cuestiones relacionadas