I just introduced multithreading into my app JUST to get a silly UIActivityIndicatorView to work. Well, the activity indicator works, alright -- but now my app crashes sometimes, and sometimes it doesn't -- under otherwise controlled conditions... I need to figure this out but don't know where to start looking...
So, what are some common mistakes beginners often make with multithreading on the iPhone? Please be specific in your answers. Thanks for your time.
UPDATE: I added my problematic source for reference.
//--------------------Where the multithreading starts------------------------
-(IBAction)processEdits:(id)sender
{
//Try to disable the UI to prevent user from launching duplicate threads
[self.view setUserInteractionEnabled:NO];
//Initialize indicator (delcared in .h)
myIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(155, 230, 20, 20)];
myIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
[self.view addSubview:myIndicator];
[self.view bringSubviewToFront:myIndicator];
[myIndicator startAnimating];
//Prepare and set properties of the NEXT modal view controller to switch to
controller = [[EndViewController alloc] initWithNibName:@"EndViewController" bundle:nil];
controller.delegate = self;
[self performSelectorInBackground:@selector(threadWork:) withObject:nil];
}
//-----------------------------THE THREAD WORK--------------------------------
-(IBAction)threadWork:(id)sender{
NSAutoreleasePool * pool;
NSString * status;
pool = [[NSAutoreleasePool alloc] init];
assert(pool != nil);
//The image processing work that takes time
controller.photoImage = [self buildPhoto];
//Stop the UIActivityIndicatorView and launch next modal view
[self performSelectorOnMainThread:@selector(stopSpinner:)withObject:nil waitUntilDone:NO];
[pool drain];
}
//-------------------Most of the WORKLOAD called in above thread ------------------------
-(UIImage*)buildPhoto
{
/*
This is the work performed in the background thread. Process photos that the user has edited and arrange them into a UIView to be finally flattened out into a new UIImage. Problem: UI usually changes for some reason during this work.
*/
UIView* photoContainerView = [[UIView alloc] initWithFrame:CGRectMake(0,0,975,1300)];
photoContainerView.backgroundColor = [UIColor whiteColor];
UIImage* purikuraFlattened;
int spacerX = 10;
int spacerY = 10;
switch (myPattern) {
case 0:
photoContainerView.frame = CGRectMake(0, 0, 320, 427);
layoutSingle = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x,photoContainerView.frame.origin.y,320,427)];
[photoContainerView addSubview:layoutSingle];
layoutSingle.image = editPhotoData1;
break;
case 1:
layoutAimg1 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY, 427, 320)];
layoutAimg2 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY, 427, 320)];
layoutAimg3 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+320, 427, 320)];
layoutAimg4 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+320, 427, 320)];
layoutAimg5 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+(320*2), 427, 320)];
layoutAimg6 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+(320*2), 427, 320)];
layoutAimg7 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX, photoContainerView.frame.origin.y+spacerY+(320*3), 427, 320)];
layoutAimg8 = [[UIImageView alloc] initWithFrame:CGRectMake(photoContainerView.frame.origin.x+spacerX+427, photoContainerView.frame.origin.y+spacerY+(320*3), 427, 320)];
[photoContainerView addSubview:layoutAimg1];
[photoContainerView addSubview:layoutAimg2];
[photoContainerView addSubview:layoutAimg3];
[photoContainerView addSubview:layoutAimg4];
[photoContainerView addSubview:layoutAimg5];
[photoContainerView addSubview:layoutAimg6];
[photoContainerView addSubview:layoutAimg7];
[photoContainerView addSubview:layoutAimg8];
if(myShots == 1){
rotPhoto1 = [self rotateImage:editPhotoData1.size:editPhotoData1];
layoutAimg1.image = rotPhoto1;
layoutAimg2.image = rotPhoto1;
layoutAimg3.image = rotPhoto1;
layoutAimg4.image = rotPhoto1;
layoutAimg5.image = rotPhoto1;
layoutAimg6.image = rotPhoto1;
layoutAimg7.image = rotPhoto1;
layoutAimg8.image = rotPhoto1;
}else if(myShots == 2){
rotPhoto1 = [self rotateImage:editPhotoData1.size: editPhotoData1];
rotPhoto2 = [self rotateImage:editPhotoData2.size: editPhotoData2];
layoutAimg1.image = rotPhoto1;
layoutAimg2.image = rotPhoto2;
layoutAimg3.image = rotPhoto2;
layoutAimg4.image = rotPhoto1;
layoutAimg5.image = rotPhoto1;
layoutAimg6.image = rotPhoto2;
layoutAimg7.image = rotPhoto2;
layoutAimg8.image = rotPhoto1;
}else if(myShots == 4){
rotPhoto1 = [self rotateImage:editPhotoData1.size: editPhotoData1];
rotPhoto2 = [self rotateImage:editPhotoData2.size: editPhotoData2];
rotPhoto3 = [self rotateImage:editPhotoData3.size: editPhotoData3];
rotPhoto4 = [self rotateImage:editPhotoData4.size: editPhotoData4];
layoutAimg1.image = rotPhoto1;
layoutAimg2.image = rotPhoto2;
layoutAimg3.image = rotPhoto3;
layoutAimg4.image = rotPhoto4;
layoutAimg5.image = rotPhoto1;
layoutAimg6.image = rotPhoto2;
layoutAimg7.image = rotPhoto3;
layoutAimg8.image = rotPhoto4;
}
break;
}
UIGraphicsBeginImageContext(photoContainerView.bounds.size);
[purikuraContainerView.layer renderInContext:UIGraphicsGetCurrentContext()];
photoFlattened = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSEnumerator *enumerator = [[photoContainerView subviews] objectEnumerator];
id object;
while ((object = [enumerator nextObject])) {
[object removeFromSuperview];
}
[photoContainerView release];
photoContainerView = nil;
if(rotPhoto1 != nil){
[rotPhoto1 release];
rotPhoto1 = nil;
}
if(rotPhoto2 != nil){
[rotPhoto2 release];
rotPhoto2 = nil;
}
if(rotPhoto3 != nil){
[rotPhoto3 release];
rotPhoto3 = nil;
}
if(rotPhoto4 != nil){
[rotPhoto4 release];
rotPhoto4 = nil;
}
if(rotPhotoSm1 != nil){
[rotPhotoSm1 release];
rotPhotoSm1 = nil;
}
if(rotPhotoSm2 != nil){
[rotPhotoSm2 release];
rotPhotoSm2 = nil;
}
if(rotPhotoSm3 != nil){
[rotPhotoSm3 release];
rotPhotoSm3 = nil;
}
if(rotPhotoSm4 != nil){
[rotPhotoSm4 release];
rotPhotoSm4 = nil;
}
return photoFlattened;
}
//-----------------------------STOP THE UIACTIVITYINDICATORVIEW---------------------
-(IBAction)stopSpinner:(id)sender
{
[self.view setUserInteractionEnabled:YES];
[myIndicator stopAnimating];
[myIndicator release];
myIndicator = nil;
if(myPattern == 0){
NSLog(@"SINGLE-SHOT MODE");
controller.isSingleShot = TRUE;
}else{
NSLog(@"MULTI-SHOT MODE");
controller.isSingleShot = FALSE;
}
controller.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:controller animated:YES];
[controller release];
[allStamps removeAllObjects];
[imageFrames removeAllObjects];
switch (myShots) {
case 1:
[editPhotoData1 release];
break;
case 2:
[editPhotoData1 release];
[editPhotoData2 release];
break;
case 4:
[editPhotoData1 release];
[editPhotoData2 release];
[editPhotoData3 release];
[editPhotoData4 release];
break;
}
/* This is the edited photo that has been onscreen. Processing is now done so it is okay to release it. The UI should be updated and now have a blank, black background instead of the image.
*/
editedPhoto.image = nil;
[editedPhoto release];
editedPhoto = nil;
}