保存一对多实体时出现神奇记录异常

Posted

技术标签:

【中文标题】保存一对多实体时出现神奇记录异常【英文标题】:Magical Record exception while save One-To-Many Entity 【发布时间】:2014-06-29 09:11:29 【问题描述】:

在我的新应用中,我有 Core Data 和 Magical Record,这就是数据库的结构:

这是实体NEWS对应的类:

@class SMCategories;

@interface SMNews : NSManagedObject

@property (nonatomic, retain) NSNumber * wpId;
@property (nonatomic, retain) NSString * title;
@property (nonatomic, retain) NSString * content;
@property (nonatomic, retain) NSString * url;
@property (nonatomic, retain) NSString * thumbnailUrl;
@property (nonatomic, retain) NSString * thumbnailFile;
@property (nonatomic, retain) NSDate * date;
@property (nonatomic, retain) SMCategories *category;

这是实体CATEGORIES对应的类:

@interface SMCategories : NSManagedObject

@property (nonatomic, retain) NSNumber * wpId;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSDate * lastUpdate;
@property (nonatomic, retain) NSSet *news;
@end

@interface SMCategories (CoreDataGeneratedAccessors)

- (void)addNewsObject:(NSManagedObject *)value;
- (void)removeNewsObject:(NSManagedObject *)value;
- (void)addNews:(NSSet *)values;
- (void)removeNews:(NSSet *)values;

这是我用来在一个循环内保存所有应该保存在实体 CATEGORIES 中的数据的函数:

- (void)saveUpdateCategories:(NSString *)result 

    NSDictionary *tmpCategories = [result objectFromJSONString];
    NSArray *categoriesList = [tmpCategories objectForKey:@"categories"];
    NSDictionary *singleCategory;

    NSPredicate * filter = [[NSPredicate alloc] init];

    for(int i = 0; i < [categoriesList count];i++) 
        singleCategory = [categoriesList objectAtIndex:i];
        filter = [NSPredicate predicateWithFormat:@"wpId == %@",[singleCategory objectForKey:@"id"]];
        SMCategories *checkCategory = [SMCategories MR_findFirstWithPredicate:filter];

        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 
            SMCategories *categoryToAdd;

            if (checkCategory == nil) 
                categoryToAdd = [SMCategories MR_createEntityInContext: localContext];
             else 
                categoryToAdd = checkCategory;
            

            categoryToAdd.name = [singleCategory objectForKey:@"name"];
            categoryToAdd.wpId = [currentUtils stringToNumber:[singleCategory objectForKey:@"id"]];
            categoryToAdd.lastUpdate = [currentUtils stringToDate:@"01/01/2000 00:00:01" formatted:@"dd/MM/yyyy HH:mm:ss"];
            categoryToAdd.news = nil;
         completion:^(BOOL success, NSError *error) 
            if (i == [categoriesList count] - 1) 
                [progressWheel stopAnimating];
                NSMutableDictionary *tmpOptions = [[NSMutableDictionary alloc] init];
                tmpOptions = [currentUtils getDictionaryPlistFile:@"options.plist" path:[currentUtils getMainLocalPath]];
                [tmpOptions setObject:[NSDate date] forKey:@"lastCategoriesUpdates"];
                [currentUtils saveDictionaryPlistFile:tmpOptions fileName:@"options.plist" path:[currentUtils getMainLocalPath]];
                [menuButton setEnabled:YES];

                if (categoryToLoadPassed == nil)
                categoryToLoadPassed = [SMCategories MR_findFirstByAttribute:@"name" withValue:DEFAULT_CATEGORY];

                [self downloadNewsOfCategory:categoryToLoadPassed];
            
        ];

    


这部分代码也可以。

这是我用来在另一个循环中保存应该插入实体 NEWS 的所有数据的函数。

-(void) saveNewsOfCategory:(SMCategories *)category resultToSave: (NSString *) result 

    NSDictionary *tmpNews = [result objectFromJSONString];
    NSArray *newsList = [tmpNews objectForKey:@"posts"];
    NSDictionary *singleNew;

    NSPredicate * filter = [[NSPredicate alloc] init];

    for(int i = 0; i < [newsList count];i++) 
        singleNew = [newsList objectAtIndex:i];
        filter = [NSPredicate predicateWithFormat:@"wpId == %@",[singleNew objectForKey:@"id"]];
        SMNews *checkNew = [SMNews MR_findFirstWithPredicate:filter];

        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

            SMNews *newToAdd;
            if (checkNew == nil) 
                newToAdd = [SMNews MR_createEntityInContext: localContext];
             else 
                newToAdd = checkNew;
            

            newToAdd.category = category;
            newToAdd.title = [singleNew objectForKey:@"title"];
            newToAdd.content = [singleNew objectForKey:@"content"];
            newToAdd.wpId = [currentUtils stringToNumber:[singleNew objectForKey:@"id"]];
            newToAdd.date = [currentUtils stringToDate:[singleNew objectForKey:@"date"] formatted:@"yyyy-MM-dd HH:mm:ss"];
            newToAdd.url = [NSString stringWithFormat:@"%@?p=%@",WEBSITE,[singleNew objectForKey:@"id"]];
            newToAdd.thumbnailFile = nil;
            newToAdd.thumbnailUrl = [singleNew objectForKey:@"image"];
         completion:^(BOOL success, NSError *error) 
            if (i == [newsList count] - 1) 

                [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 

                    category.lastUpdate = [NSDate date];

                 completion:^(BOOL success, NSError *error) 

                    [progressWheel stopAnimating];
                    [currentTable reloadData];
                ];

            
        ];

    


这里它在行旁边抛出了一个异常EXT_BAD_ACCESS

newToAdd.category = category;

类别作为相应函数的参数之一传递。

正如您所理解的那样,我有 2 个具有一对多关系的 entitis CATEGORIES 和 NEWS。 首先,当用户想要下载特定类别的所有新闻时,我下载所有类别并将它们存储到 CATEGORIES 实体中作为我函数的参数。 如果删除犯罪行,则所有新闻都正确存储在数据库中,但它们与任何类别无关。

我做错了什么?

【问题讨论】:

【参考方案1】:

以下行引发异常,因为我将实体保存在新线程中。

newToAdd.category = category;

所以使用单例类来获取类别并以这种方式设置在特定的本地上下文中

SMCategories *currentCategory = [[DataManager getInstance] getCurrentCategory];

newsToAdd.category = [currentCategory MR_inContext:localContext];

现在一切正常。

【讨论】:

以上是关于保存一对多实体时出现神奇记录异常的主要内容,如果未能解决你的问题,请参考以下文章

如果同时保存的两个实体父子实体映射为一对多关系,则抛出 id not found 父类异常

神奇的记录删除所有孩子的一对多关系

一对多的神奇记录

序列化对象时出现循环引用错误。一对多关联对象

讨教:hibernate一对多查询时出异常

在dapper中使用一对多关系时出现splitOn错误