2011-04-05 87 views
0

我想继承我的NSManagedObject类来封装我的get,set,save例程。该类使用自己的managedObjectContext和共享的persistentStoreCoordinator,因为这需要线程安全。NSManagedObject的子类导致NSInvalidArgumentException

所有没有问题的方法调用,但是当我试图执行保存:方法,我得到以下错误:

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假设也许我与继承的方法干扰没有成功。

任何帮助,非常感谢。

+0

你可以发送一个测试消息myEntity像一个富方法? – 2011-04-05 20:30:50

+0

要查看它是否响应?我可以尝试,但它会响应init,所以我会认为这将是相同的。 – 2011-04-05 20:31:49

+0

有趣的是,我添加了以下方法: - (无效)的东西,它也抛出错误。然而,初始化工作。 – 2011-04-05 20:33:08

回答

1

这是一个糟糕的内存管理使用init函数的方式作为调用alloc的方式在init之前。同样,虽然你正在改变自己的价值,但它并不是XXMyEntity的类型,它仍然是MyEntity,这就是为什么你会收到错误。

更新

要获得XXMyEntity工作,你需要打开你的xcdatamodel文件和你的myEntity所级设置为XXMyEntity。还读通过NSManagedObject的Subclassing Notes

+0

+1这样的init函数通常注定是没有的打电话给超级。 – TechZen 2011-04-05 21:23:13

2

我认为你的主要问题是你的init方法要求对象已经有一个托管对象的上下文,即使你从来没有指定它。当然,你不能指定它,因为之前init自己不存在。悖论的位。正如乔指出的那样,无论如何你都在使用错误的实体。

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

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上下文。

如果你试图使用核心数据奇迹般地坚持应用程序启动之间的事情,你做的大部分在主线程的处理中,我有一些建议:

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

    +(myEntity所)实体 { 的NSManagedObjectContext *上下文= ...; MyEntity * entity = [NSEntityDescription insert ...]; 退货实体; }

带警告:

  • 你仍然需要一种方法来获取实体(你可能会提供更多的便利构造做到这一点)
  • 你仍然需要删除的对象,以避免数据库变得巨大。
  • 储蓄相当缓慢。
  • 如果您决定在后台线程中进行保存,因此它不会阻止用户界面,您需要仔细检查的每一段代码,因为NSManagedObject和NSManagedObjectContext不是线程安全的,因此会触及MyEntity。

另请注意,yhere的情况下在同一个线程有多个MOCS其中是有道理的:如果你有一个取消按钮上的“编辑”视图中,可以对编辑画面单独MOC并没有保存,如果用户取消。或者,我认为你可以使用单个MOC和NSUndoManager。

Core Data Programming Guide: Technology Overview给出了核心数据是和不是的正确列表;我怀疑你没有按照预期使用它。