views:

43

answers:

2

Hi, Im having some trouble passing an NSNumber object to different threads. I call a function on viewDidload that loads up some objects from core data as a background process. which calls another function which loops through the loaded objects to see if there are any images associated with it alredy downloaded. if its not present, download the images asynchronously and save it locally. The thing is I need to perform startDownloadFor:atIndex: on the main thread. But the application crashes because of the NSNumber object thats being passed. here is the code..

- (void)viewDidLoad {
   ...
   ...
   [self performSelectorInBackground:@selector(loadImages) withObject:nil]; 
}

-(void)loadImages{
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 ...
        ...
 [self fillInImages];
 [pool release];
}

-(void)fillInImages{

 NSString *imageURL;
 for (int i=0; i < [dataManager.objectList count]; i++) {
  ...
  if ([dataManager.RelatedImages Image] == nil) {
   //[self startDownloadFor:imageURL atIndex:[NSNumber numberWithInt:i]; // << WORKS FINE
   [self performSelectorOnMainThread:@selector(startDownloadFor:atIndex:) withObject:(imageURL, [NSNumber numberWithInt:i]) waitUntilDone:YES]; // << CRASHES 
   ...
  }else {
   ...
  }
  ...
 }
 ...
}

-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{

 NSString *indexKey = [NSString stringWithFormat:@"key%d",indexPath];
 ...
}

what is the right way of doing this?

Thanks

A: 

I've never seen that syntax passing more than one object to a selector - is that valid objective-c code? also, in your startDownloadFor:atIndex: you're passing in an NSNumber but the type for the second parameter on that selector is (int) - that can't be good ;)

The docs for performSelectorOnMainThread: say that the selector should take only one argument of type id. You're passing an invalid selector so I think that it's getting very confused about where the NSNumber is.

To fix it, pass an NSDictionary conatining the number and the image URL i.e.

NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:imageURL, @"imageURL", [NSNumber numberWithInt:i], @"number", nil];
[self performSelectorOnMainThread:@selector(startDownload:) withObject:dict waitUntilDone:YES];

and

//-(void)startDownloadFor:(NSString*)imageUrl atIndex:(int)indexPath{
- (void)startdownload:(NSDictionary *)dict {
    NSURL *imageURL = [dict objectForKey:@"imageURL"];
    int indexPath = [[dict objectforKey:@"number"] intValue];
deanWombourne
A: 

You are trying to pass 2 arguments into performSelectorOnMainThread:withObject:waitUntilDone: while the method only supports passing one argument.

You need to use NSInvocation to send more arguments (or use an NSDictionary like dean proposed).

SEL theSelector;
NSMethodSignature *aSignature;
NSInvocation *anInvocation;

theSelector = @selector(startDownloadFor:atIndex:);
aSignature = [self instanceMethodSignatureForSelector:theSelector];
anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
[anInvocation setSelector:theSelector];
[anInvocation setTarget:self];
// indexes for arguments start at 2, 0 = self, 1 = _cmd
[anInvocation setArgument:&imageUrl atIndex:2];
[anInvocation setArgument:&i atIndex:3];

[anInvocation performSelectorOnMainThread:@selector(invoke) withObject:NULL waitUntilDone:YES];
Jason Harwig