NSManagedObject子类导致NSInvalidArgumentException

时间:2011-04-05 20:24:17

标签: iphone objective-c core-data subclass

我试图将我的NSManagedObject类子类化为封装我的get,set,save例程。该类使用自己的managedObjectContext和共享的persistentStoreCoordinator,因为这需要是线程安全的。

所有方法调用都没有问题,但是当我尝试执行save:方法时,我收到以下错误:

NSInvalidArgumentException', reason: '**-[MyEntity save:]: unrecognized selector sent to instance**'

附件是一个提供相同错误的简化版本。

这是子类的代码:

@interface XXMyEntity : MyEntity {
@private
    NSManagedObjectContext * _managedObjectContext;
}

- (XXMyEntity *) init;
- (BOOL) save:(NSError **)error;

- (NSManagedObjectContext *) managedObjectContext;
- (NSPersistentStoreCoordinator *) persistentStoreCoordinator;

@end

@implementation XXMyEntity

- (XXMyEntity *) init 
{
    self = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

    return self;
}

- (BOOL) save:(NSError **)error
{
    return [[self managedObjectContext] save:error];
}

- (NSManagedObjectContext *)managedObjectContext {

    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}

- (NSPersistentStoreCoordinator *) persistentStoreCoordinator 
{
    newCoreDataAppDelegate * appDelegate = (newCoreDataAppDelegate *)[[UIApplication sharedApplication] delegate];
    return appDelegate.persistentStoreCoordinator;
}

- (void) dealloc
{
    [_managedObjectContext release];

    [super dealloc];
}
@end

实施:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.

    XXMyEntity * myEntity = [[XXMyEntity alloc]init];
    myEntity.id = [NSNumber numberWithInt:1];
    myEntity.title = @"My Title";

    [myEntity save:nil];

    [self.window makeKeyAndVisible];
    return YES;
}

我还尝试将方法签名更改为其他类似saveEntity,假设我可能干扰了继承的方法但没有成功。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:2)

我认为您的主要问题是您的init方法要求对象已经拥有托管对象上下文,即使您从未分配过它。当然,你不能指定一个因为init之前不存在自我。有点悖论。正如乔所指出的那样,你正在使用错误的实体。

您不应该以这种方式初始化托管对象子类。只需将它们插入到上下文中,就像通用托管对象一样,上下文将足够智能以返回正确的子类。如果要进行自定义,请使用awakeFromInsert方法执行此操作。

答案 1 :(得分:1)

使用init函数的内存管理很差,就像在init之前调用alloc一样。此外,虽然您要更改值self,但它不会使其成为XXMyEntity的类型,但它仍然是MyEntity,这就是您收到错误的原因。

<强>更新

要使XXMyEntity工作,您需要打开xcdatamodel文件并将MyEntity类设置为XXMyEntity。另请阅读NSManagedObject的Subclassing Notes

答案 2 :(得分:0)

让我发现它有些不对劲......

  • 我假设MyEntity是NSManagedObject的子类。 XXXMyEntity有什么意义?

  • - (NSManagedObjectContext *) managedObjectContext;

    • NSManagedObject已经有一个方法-managedObjectContext。覆盖它可能会搞砸。
    • 您将为XXXMyEntity的每个实例返回不同的NSManagedObjectContext。不同上下文中的托管对象不能形成彼此之间的关系(好吧,他们可以,但坏事发生)。
  • self = [NSEntityDescription insertNewObjectForEntityForName:@"MyEntity" inManagedObjectContext:[self managedObjectContext]];

    • 你正在泄漏旧的自我(A)。
    • 你没有保留新的自我(B)。
    • 您正在调用[self managedObjectContext](A),但是将新对象(B)插入到A的managedObjectContext中。但是,由于您已覆盖-managedObjectContext,[b managedObjectContext]将返回不同的上下文。这意味着对b的任何更改可能无法正确获取;这很难说。
    • +insertNewObjectForEntityForName:inManagedObjectContext:的调用不会返回XXXMyEntity的实例(由TechZen识别)。
    • 即使这样做,+insertNewObjectForEntityForName:inManagedObjectContext:也会使用+alloc-init构建托管对象。你已经覆盖了-init,所以它会导致无限递归。
  • newCoreDataAppDelegate * appDelegate = (newCoreDataAppDelegate *)[[UIApplication sharedApplication] delegate];

    • UIKit主要不是线程安全的,只能从主线程访问。从不是主线程的线程调用-[UIApplication delegate]可能很愚蠢。如果线程主线程,那么你可能想要使用相同的NSManagedObject上下文。

如果您尝试使用Core Data在应用启动之间神奇地保留内容,并且您在主线程上执行了大部分处理,我会提出一些建议:

  • 为整个应用分配单个NSManagedObjectContext。您可能希望将其存储在应用程序委托或“单身”或其他内容中。
  • 使用“便利构造函数”而不是覆盖-init:

    +(myEntity所)实体 {     NSManagedObjectContext * context = ...;     MyEntity * entity = [NSEntityDescription insert ...];     回报实体; }

有警告:

  • 您仍然需要一种获取实体的方法(您可以使用更方便的构造函数执行此操作)
  • 您仍然需要删除对象以避免数据库变得庞大。
  • 保存很慢。
  • 如果您决定在后台线程中进行保存,以便它不会阻止UI,则需要仔细检查触及MyEntity的每一段代码,因为NSManagedObject和NSManagedObjectContext不是线程-safe。

另请注意,在同一个线程中有多个MOC的情况是有意义的:如果您有一个带有取消按钮的“编辑”视图,您可以为编辑屏幕设置单独的MOC,如果用户取消则不保存。或者,我认为您可以使用单个MOC和NSUndoManager。

Core Data Programming Guide: Technology Overview给出了一个核心数据是否合适的列表;我怀疑你没有按照预期使用它。