пытается обновить NSManagedObject в ASIHTTPRequest / фоновом потоке - PullRequest
0 голосов
/ 21 января 2012

как говорится в теме - я пытаюсь обновить NSManagedObject в другой теме.приложение для моего iPhone загружает несколько Asset и хочет обновить downloadStatus указанного объекта.из-за двух разных потоков я создаю новый NSManagedObjectContext и выбираю Актив в методе main моих NSOperation / ASIHTTPRequest.

первых 1-n Asset sзагружаются без проблем, но затем я получаю EXC_BAD_ACCESS при попытке сохранить контекст.

вот мой код

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

и 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

и именно здесь происходит ОШИБКА (в requestFinished:)

NSError *sError;
[self.ctx save:&sError];

, может быть, кто-то может объяснить мне, почему это происходит!?

1 Ответ

0 голосов
/ 23 января 2012

хорошо, я полностью переработал и перенес обновление в класс Asset.

вместо извлечения Asset с ObjectID я просто передаю его в класс AssetDownload и выполняю

- (void) updateDownloadStatus:(TCRDownloadStatus)status
{
    NSNumber *statusNum = [NSNumber numberWithInt:status];
    [self.asset performSelectorOnMainThread:@selector(updateDownloadStatus:)    
                                 withObject:statusNum
                              waitUntilDone:YES];
}

при каждом изменении статуса.

отлично работает до сих пор ... и гораздо меньше кода, чем раньше :)

...