Multithreading problem: code crashes at specific point
I just introduced multithreading into my app, and now it crashes at a specific point in the program, whilst if I don't do multithreading it does work.
FlipsideViewController.h
//
// FlipsideViewController.h
// iRSG
//
// Created by Thomas van Arkel on 12-02-11.
// Copyright 2011 Arques Software. All rights reserved.
//
#import <UIKit/UIKit.h>
@protocol FlipsideViewControllerDelegate;
@interface FlipsideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
NSArray *timeTableArray;
IBOutlet UIActivityIndicatorView *activityIndicator;
}
- (IBAction)getTimeTable:(UIButton *)sender;
- (IBAction)removeTimeTable:(UIButton *)sender;
@property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
@end
@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end
FlipsideViewController.m
//
// FlipsideViewController.m
// iRSG
//
// Created by Thomas van Arkel on 12-02-11.
// Copyright 2011 Arques Software. All rights reserved.
//
#import "FlipsideViewController.h"
#import "TimeTableCreator.h"
#import "Lesson.h"
#import "iRSGAppDelegate.h"
@interface FlipsideViewController()
@property (nonatomic, retain) IBOutlet UIActivityIndicatorView *spinner;
@end
@implementation FlipsideViewController
@synthesize delegate;
@synthesize spinner;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor groupTableViewBackgroundColor];
}
- (IBAction)done:(id)sender {
[self.delegate flipsideViewControllerDidFinish:self];
}
- (IBAction)getTimeTable:(UIButton *)sender {
activityIndicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.frame = CGRectMake(0.0, 0.0, 40.0, 40.0);
activityIndicator.center = self.view.center;
[self.view addSubview: activityIndicator];
[activityIndicator startAnimating];
dispatch_queue_t downloadQueue = dispatch_queue_create("TimeTable downloader", NULL);
timeTableArray = [[NSArray alloc] init];
dispatch_async(downloadQueue, ^{
NSString* settingValue = [[NSUserDefaults standardUserDefaults] stringForKey:@"partOfSchool"];
if ([settingValue isEqualToString:@"upper"]) {
NSString *studentID = [[NSUserDefaults standardUserDefaults] stringForKey:@"studentID"];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://roosters.rsgslingerboslevant.nl/lichtkrant/slingerbos/leerlingen/ll_roost_%@.htm", studentID]];
timeTableArray = [TimeTableCreator createTimeTableWithURL:url];
} else if ([settingValue isEqualToString:@"lower"]) {
NSString *class = [[NSUserDefaults standardUserDefaults] stringForKey:@"class"];
if (class.length == 2) {
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://roosters.rsgslingerboslevant.nl/lichtkrant/Slingerbos/Klassen/K_%@.htm", class]];
timeTableArray = [TimeTableCreator createTimeTableForLowerClassWithURL:url];
} else if (class.length == 3) {
NSString *beginningString = [class substringToIndex:1];
NSString *endString = [class substringFromIndex:2];
endString = [endString lowercaseString];
class = [beginningString stringByAppendingString:endString];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://roosters.rsgslingerboslevant.nl/lichtkrant/Slingerbos/Klassen/K_%@.htm", class]];
timeTableArray = [TimeTableCreator createTimeTableForLowerClassWithURL:url];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext *managedObjectContext = nil;
if (managedObjectContext == nil)
{
iRSGAppDelegate *appDelegate = (iRSGAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
}
NSFetchRequest * allLessons = [[NSFetchRequest alloc] init];
[allLessons setEntity:[NSEntityDescription entityForName:@"Lesson" inManagedObjectContext:managedObjectContext]];
[allLessons setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * lessons = [managedObjectContext executeFetchRequest:allLessons error:&error];
NSLog(@"Amount of lessons is %@", [NSNumber numberWithInt:[lessons count]]);
[allLessons release];
//error handling goes here
for (NSManagedObject * lesson in lessons) {
[managedObjectContext deleteObject:lesson];
}
iRSGAppDelegate *appDelegate = (iRSGAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate saveContext];
[appDelegate saveContext];
for (NSArray *lessonArray in timeTableArray) {
[Lesson lessonWithLessonArray:lessonArray inManagedObjectContext:managedObjectContext];
}
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:@"Het rooster is succesvol geïmporteerd"delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
[activityIndicator stopAnimating];
});
});
dispatch_release(downloadQueue);
}
- (IBAction)removeTimeTable:(UIButton *)sender {
NSManagedObjectContext *managedObjectContext = nil;
if (managedObjectContext == nil) {
iRSGAppDelegate *appDelegate = (iRSGAppDelegate *)[[UIApplication sharedApplication] delegate];
managedObjectContext = [appDelegate managedObjectContext];
}
NSFetchRequest * allLessons = [[NSFetchRequest alloc] init];
[allLessons setEntity:[NSEntityDescription entityForName:@"Lesson" inManagedObjectContext:managedObjectContext]];
[allLessons setIncludesPropertyValues:NO]; //only fetch the managedObjectID
NSError * error = nil;
NSArray * lessons = [managedObjectContext executeFetchRequest:allLessons error:&error];
NSLog(@"Amount of lessons is %@", [NSNumber numberWithInt:[lessons count]]);
[allLessons release];
//error handling goes here
for (NSManagedObject * lesson in lessons) {
[managedObjectContext deleteObject:lesson];
}
iRSGAppDelegate *appDelegate = (iRSGAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate saveContext];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation =开发者_Python百科= UIInterfaceOrientationPortrait);
}
*/
- (void)dealloc {
[super dealloc];
}
@end
The program always crashes here
for (NSArray *lessonArray in timeTableArray) {
[Lesson lessonWithLessonArray:lessonArray inManagedObjectContext:managedObjectContext];
}
Thanks in advance
EDIT Source code http://cl.ly/3k322a472A0Q0A3F0S1G
To test it, in settings app enter '2007042' in for 'leerlingnummer' and for 'onder/bovenbouw' choose 'bovenbouw' (sorry for the fact that it is in Dutch, I'm writing the code for a Dutch school
I get a EXC_BAD_ACCESS error, nothing is on the console. SOLVED I made a new timeTableArray property and when setting the array, i used self.timeTableArray. This was why it said that it was deallocated (thanks to NSZombie)
The reason is most likely that you are using the NSManagedObjectContext
which was created from a different thread. Core Data requires to create the NSManagedObjectContext
in the same thread where it is used, i.e. if you want to access core data from 2 threads (or dispatch queues) you must create 2 NSManagedObjectContext's
(which can then use the same NSPersistentStore
).
You might have problems getting a good answer to this question, as the general answer is "Your code is just not thread safe". I know this isn't that helpful but it's a big topic (fills books) and you will be lucky to have it answered on SO. If you don't know what it means to be thread safe then you should do some reading. There are some good docs on the apple website, see for example
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/Multithreading/Introduction/Introduction.html
Looking at your code i see many problems, but for example you appear to be creating timeTableArray in one thread, while at the same time time trying to use it in another thread. How can this work? Which happens first? You have no way of knowing, and it can easily be different everytime it is run. If this really is news to you (apologies if it isn't) a rough rule of thumb would be - you can't do any Core Data from a background thread, you can't do any GUI from a background thread, you can't use any object which could also be being used from another thread.
In general, this kind of multithreading really is pretty difficult.
As an aside, if you are trying to download stuff on a background thread, don't do it. There are asynchronous methods for you to use. These are already multithreaded.
[Lesson lessonWithLessonArray:lessonArray inManagedObjectContext:managedObjectContext];
It always crashes there, huh? How about some code? How did you implement lessonWithLessonArray:inManagedObjectContext:
? Why is it a class method (that seems odd)?
SOLVED I made a new timeTableArray property and when setting the array, i used self.timeTableArray. This was why it said that it was deallocated (thanks to NSZombie)
精彩评论