CoreData是否允许对象图循环? (奇怪的错误)

时间:2011-05-05 12:12:54

标签: objective-c ios core-data nsmanagedobject

我一直在与CoreData有一段奇怪的问题。这将是一个长长的问题,所以请耐心等待......

我有两个Entites,称他们为A和B.B可能是几个A的创建者和更新者。 A必须有B作为创建者,并且可能有也可能没有B作为更新者。 (必需和可选关系。)

B可能同时创建和更新零个或多个A.

With inverse relationships

以上是我建立的数据模型。 (我真的不需要从B到A的反向关系,但是CoreData给了我编译时警告。)

修改

以下是ManagedObject子类的相关部分:

A.H

@property (nonatomic, retain) B * creator;
@property (nonatomic, retain) B * updater; 

A.M

@dynamic creator;
@dynamic updater;

B.h

@property (nonatomic, retain) NSSet * created;
@property (nonatomic, retain) NSSet * updated;

B.m

@dynamic updated;
@dynamic created;

- (void)addUpdatedObject:(A *)value { NSSet *changedObjects = [[NSSet
alloc] initWithObjects:&value count:1]; [self
willChangeValueForKey:@"updated"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:changedObjects]; [[self
primitiveValueForKey:@"updated"] addObject:value]; [self
didChangeValueForKey:@"updated"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:changedObjects]; [changedObjects release]; }

- (void)removeUpdatedObject:(A *)value { NSSet *changedObjects =
[[NSSet alloc] initWithObjects:&value count:1]; [self
willChangeValueForKey:@"updated"
withSetMutation:NSKeyValueMinusSetMutation
usingObjects:changedObjects]; [[self
primitiveValueForKey:@"updated"] removeObject:value]; [self
didChangeValueForKey:@"updated"
withSetMutation:NSKeyValueMinusSetMutation
usingObjects:changedObjects]; [changedObjects release]; }

- (void)addUpdated:(NSSet *)value { [self
willChangeValueForKey:@"updated"
withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
[[self primitiveValueForKey:@"updated"] unionSet:value]; [self
didChangeValueForKey:@"updated"
withSetMutation:NSKeyValueUnionSetMutation usingObjects:value]; }

- (void)removeUpdated:(NSSet *)value { [self
willChangeValueForKey:@"updated"
withSetMutation:NSKeyValueMinusSetMutation usingObjects:value];
[[self primitiveValueForKey:@"updated"] minusSet:value]; [self
didChangeValueForKey:@"updated"
withSetMutation:NSKeyValueMinusSetMutation usingObjects:value]; }

- (void)addCreatedObject:(A *)value { NSSet *changedObjects = [[NSSet
alloc] initWithObjects:&value count:1]; [self
willChangeValueForKey:@"created"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:changedObjects]; [[self
primitiveValueForKey:@"created"] addObject:value]; [self
didChangeValueForKey:@"created"
withSetMutation:NSKeyValueUnionSetMutation
usingObjects:changedObjects]; [changedObjects release]; }

- (void)removeCreatedObject:(A *)value { NSSet *changedObjects =
[[NSSet alloc] initWithObjects:&value count:1]; [self
willChangeValueForKey:@"created"
withSetMutation:NSKeyValueMinusSetMutation
usingObjects:changedObjects]; [[self
primitiveValueForKey:@"created"] removeObject:value]; [self
didChangeValueForKey:@"created"
withSetMutation:NSKeyValueMinusSetMutation
usingObjects:changedObjects]; [changedObjects release]; }

- (void)addCreated:(NSSet *)value { [self
willChangeValueForKey:@"created"
withSetMutation:NSKeyValueUnionSetMutation usingObjects:value];
[[self primitiveValueForKey:@"created"] unionSet:value]; [self
didChangeValueForKey:@"created"
withSetMutation:NSKeyValueUnionSetMutation usingObjects:value]; }

- (void)removeCreated:(NSSet *)value { [self
willChangeValueForKey:@"created"
withSetMutation:NSKeyValueMinusSetMutation usingObjects:value];
[[self primitiveValueForKey:@"created"] minusSet:value]; [self
didChangeValueForKey:@"created"
withSetMutation:NSKeyValueMinusSetMutation usingObjects:value]; }

结束编辑

当我创建一个以B为创建者的新A时,一切都很好(使用反向关系时)。但是,当我稍后更新相同的A,使用与创建者相同的B时,我得到以下崩溃:

2011-05-05 13:17:40.885 myapp[27033:207] -[NSCFNumber count]:
unrecognized selector sent to instance 0x5d14eb0

2011-05-05 13:17:40.887 myapp[27033:207] *** Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '-[NSCFNumber
count]: unrecognized selector sent to instance 
0x5d14eb0'

*** Call stack at first throw: ( 

0 CoreFoundation 0x014065a9
__exceptionPreprocess + 185


    1 libobjc.A.dylib 0x0155a313 objc_exception_throw + 44

    2 CoreFoundation 0x014080bb -[NSObject(NSObject)
    doesNotRecognizeSelector:] + 187

    3 CoreFoundation 0x01377966 ___forwarding___ + 966

    4 CoreFoundation 0x01377522 _CF_forwarding_prep_0 + 50

    5 CoreFoundation 0x013b7c3b -[NSSet intersectsSet:] + 59

    6 Foundation 0x00c3f4fb NSKeyValueWillChangeBySetMutation +
    422

    7 Foundation 0x00c0416d NSKeyValueWillChange + 400

    8 Foundation 0x00c3f34d
    -[NSObject(NSKeyValueObserverNotification)
    willChangeValueForKey:withSetMutation:usingObjects:] + 315

    9 CoreData 0x01172bd0 -[NSManagedObject
    willChangeValueForKey:withSetMutation:usingObjects:] + 112

    10 myapp 0x0004fd48 -[B addUpdatedObject:] + 168

    11 CoreData 0x011729f2 -[NSManagedObject(_NSInternalMethods)
    _includeObject:intoPropertyWithKey:andIndex:] + 98

    12 CoreData 0x01169301 -[NSManagedObject(_NSInternalMethods)
    _didChangeValue:forRelationship:named:withInverse:] + 497

    13 Foundation 0x00c051e4 NSKeyValueNotifyObserver + 361

    14 Foundation 0x00c04ca6 NSKeyValueDidChange + 384

    15 Foundation 0x00beb3e2
    -[NSObject(NSKeyValueObserverNotification)
    didChangeValueForKey:] + 123

    16 CoreData 0x01166adb
    _PF_ManagedObject_DidChangeValueForKeyIndex + 171

    17 CoreData 0x01165de9 _sharedIMPL_setvfk_core + 313

    18 CoreData 0x01173997 _svfk_11 + 39

(是的, B 定义了addUpdatedObject方法。)

有关更多背景信息,A:列表显示在由UITableView支持的NSFetchedResultsController中。

最奇怪的是,恕我直言,完全有可能添加一个新的A(以B作为创建者)与反向关系而不会导致崩溃,同时将相同的B作为更新者添加到同一个A导致崩溃。

但是,当我删除反向关系时,一切都按预期工作。

Without inverse relationships

我可以添加与A:s创建者和更新程序相同的B而没有任何问题,应用程序可以正常工作,除了CoreData的构建时警告:

A.creator should have an inverse
A.updater should have an inverse

那么,是否有任何CoreData专家可以解释为什么第一个案例会破裂?

2 个答案:

答案 0 :(得分:4)

你不应该能够建立双重关系。 Xcode 3.x和更早版本甚至不允许您创建具有双重关系的模型(现在只需检查Xcode 4,看起来它会让您在没有警告的情况下这样做。不是一个好主意。)

强烈建议不要使用双重关系,因为它们会严重影响图表的完整性,例如:删除规则,如果某个关系中的delete规则和另一个关系中的deny规则会发生什么?我认为双重关系的图表只是等待发生的火车残骸。

更重要的是,我从未看到真正需要这种数据模型的情况。通常,更好的数据模型设计将消除对这种危险设置的明显需求。在这种情况下,我认为您需要插入实体来模拟实际关系本身,因为关系实际上有行为或排序:

A{
  creator<-->Creation.a
  updater<-->Update.a
}

B{
  creations<-->>Creation.b
  updates<-->>Update.b
}

Creation{
  a<-->A.creator
  b<-->>B.cretions
}

Update{
  a<-->A.updater
  b<<-->B.updates
}

这样,您只需向Update实体添加属性,即可轻松对更新日期等内容进行建模。

答案 1 :(得分:0)

更改模型后,是否为两个实体再次创建了ManagedObject子类(通过添加反转)?

如果不这样做,模型可能会尝试向不准备接受它们的对象添加反向引用。