ysnky
ysnky

Reputation: 179

performSelectorInBackground with multiple params

How can I call a method with multiple params like below with performSelectorInBackground?

Sample method:

-(void) reloadPage:(NSInteger)pageIndex firstCase:(BOOL)firstCase;

Upvotes: 15

Views: 16471

Answers (4)

mihai
mihai

Reputation: 4214

with performSelectorInBackground you can only pass one argument, so make a custom object for this method to hold your data, itll be more concise than an ambiguous dictionary or array. The benefit of this is you can pass the same object around when done containing several return properties.

#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

and pass that object to your method:

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

make sure to clean up the object when done to prevent memory leaks

Upvotes: 1

Steve Wilford
Steve Wilford

Reputation: 9012

I've just found this question and wasn't happy with any of the answers. In my opinion neither make good use of the tools available, and passing around arbitrary information in arrays and dictionaries generally worries me.

So, I went and wrote a small NSObject category that will invoke an arbitrary selector with a variable number of arguments:

Category Header

@interface NSObject (NxAdditions)

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

@end

Category Implementation

@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

Usage

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

Upvotes: 6

Roozbeh Zabihollahi
Roozbeh Zabihollahi

Reputation: 7347

Well, I have used this:

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

for this:

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

Upvotes: 6

Tim
Tim

Reputation: 60140

The problem is that performSelectorInBackground:withObject: takes only one object argument. One way to get around this limitation is to pass a dictionary (or array) of arguments to a "wrapper" method that deconstructs the arguments and calls your actual method:

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

This way you're only passing a "single" argument to the backgrounding call, but that method can construct the multiple arguments you need for the real call (which will take place on the same backgrounded thread).

Upvotes: 38

Related Questions