like the topic states – i’m trying to update a NSManagedObject in another thread.
my iPhone app is downloading multiple Asset and wants to update the downloadStatus of the given object. because of the two different threads i’m creating a new NSManagedObjectContext and fetch the Asset in the main method of my NSOperation / ASIHTTPRequest.
the first 1-n Assets are downloaded without problems but then i get an EXC_BAD_ACCESS while trying to save the context.
here is my code
AssetDownload.h
#import "ASIHTTPRequest.h"
@interface AssetDownload : ASIHTTPRequest
{
@private
Asset *_tempAsset;
NSManagedObjectID *_assetId;
NSManagedObjectContext *_ctx;
}
@property (nonatomic, strong) NSManagedObjectID *assetId;
@property (nonatomic, strong) NSManagedObjectContext *ctx;
- (id) initWithURL:(NSURL *)assetUrl andAsset:(NSManagedObjectID *)assetId;
+ (id) requestWithURL:(NSURL *)newURL andAsset:(NSManagedObjectID *)assetId;
@end
and the AssetDownload.m
#import "AssetDownload.h"
#import "DataController.h"
@interface AssetDownload (Private)
- (void) checkArticleStatusForAsset;
@end
@implementation AssetDownload
@synthesize assetId=_assetId;
@synthesize ctx=_ctx;
- (id) initWithURL:(NSURL *)assetUrl andAsset:(NSManagedObjectID *)assetId
{
self = [self initWithURL:assetUrl];
if (self)
{
self.assetId = assetId;
}
return self;
}
//
- (void) main
{
// CORE DATA & MULTITHREADING
_ctx = [[NSManagedObjectContext alloc] init];
[self.ctx setUndoManager:nil];
[self.ctx setPersistentStoreCoordinator: [[DataController sharedInstance] persistentStoreCoordinator]];
// Register context with the notification center
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:@selector(mergeChanges:)
name:NSManagedObjectContextDidSaveNotification
object:self.ctx];
// get the asset from temp managedContext
NSError *err;
_tempAsset = (Asset *) [self.ctx existingObjectWithID:self.assetId error:&err];
if (_tempAsset == nil)
{
// asset not found
NSLog(@"download asset data not found in CoreData - cancel download");
return;
}
if ([_tempAsset isAvailable])
{
NSLog(@"AssetDownload main() >>> already downloaded -> COMPLETE");
complete = YES;
[self markAsFinished];
[self checkArticleStatusForAsset];
return;
}
NSLog(@"AssetDownload main() >>> download");
[super main];
}
//
- (void) requestFinished
{
NSLog(@"AssetDownload requestFinished() >>> %i", self.responseStatusCode);
NSError *mError;
NSFileManager *fmngr = [NSFileManager defaultManager];
if (self.responseStatusCode == 200)
{
if ([fmngr moveItemAtPath:self.downloadDestinationPath toPath:_tempAsset.localPath error:&mError])
{
NSLog(@"file moved: %@", _tempAsset.localPath);
_tempAsset.downloadStatus = DownloadStatusComplete;
[self checkArticleStatusForAsset];
}
else
{
NSLog(@"ERROR file not moved: %@ ... %@", _tempAsset.localPath, mError);
_tempAsset.downloadStatus = DownloadStatusError;
}
}
else
{
[fmngr removeItemAtPath:self.downloadDestinationPath error:nil];
_tempAsset.downloadStatus = DownloadStatusError;
}
NSError *sError;
[self.ctx save:&sError];
[super requestFinished];
}
//
- (void) failWithError:(NSError *)theError
{
NSLog(@"AssetDownload failWithError() >>> %@", theError);
_tempAsset.downloadStatus = DownloadStatusError;
[self.ctx save:nil];
[super failWithError:theError];
}
//
- (void) checkArticleStatusForAsset
{
if (_tempAsset.article.isLoaded)
{
NSDictionary *info = [NSDictionary dictionaryWithObject:_tempAsset.article forKey:@"article"];
[[NSNotificationCenter defaultCenter] postNotificationName:kNotificationArticleAssetsLoaded
object:self
userInfo:info];
}
}
#pragma mark -
- (void) mergeChanges:(NSNotification *)notification
{
if ([notification object] == self.ctx)
{
NSLog(@"MERGE !");
NSManagedObjectContext *mainContext = [[DataController sharedInstance] managedObjectContext];
// Merge changes into the main context on the main thread
[mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
}
}
#pragma mark -
//
+ (id) requestWithURL:(NSURL *)newURL andAsset:(NSManagedObjectID *)assetId andManager:(AssetManager*)manager
{
return [[self alloc] initWithURL:newURL andAsset:assetId andManager:manager];
}
@end
and this is where the ERROR happens (in the requestFinished:)
NSError *sError;
[self.ctx save:&sError];
maybe someone can explain to me why this is happening!?
ok, i refactored the whole thing and moved the update to the
Assetclass.instead of fetching the
Assetwith itsObjectIDi just pass it into theAssetDownloadclass and do awhenever the status changes.
works great so far … and is far less code than before 🙂