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

发布于 2024-11-05 13:13:22 字数 6251 浏览 4 评论 0原文

一段时间以来,我一直在使用 CoreData 时遇到一个奇怪的问题。这将是一个很长的问题,所以请耐心等待……

我有两个实体,称它们为 A 和 B。B 可能既是多个 A 的创建者又是更新者。 A 必须有 B 作为创建者,并且可能有也可能没有 B 作为更新者。 (必需的和可选的关系。)

B 可能创建并更新了零个或多个 A。

With inverse trees

以上是我构建的数据模型。 (我实际上并不需要从 B 到 A 的逆关系,但 CoreData 会给我编译时警告,否则。)

编辑

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

Ah

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

Am

@dynamic creator;
@dynamic updater;

Bh

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

Bm

@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]; }

END EDIT

当我用 B 作为创建者创建一个新的 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 时,使用 在背景中,A: 列表显示在由 NSFetchedResultsController 支持的 UITableView 中。

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

然而,当我消除反向关系时,一切都会按预期进行。

没有反向关系

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

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

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

I've been having a strange problem with CoreData for a while now. This will be a lenghty question, so bear with me...

I have two Entites, call them A and B. B might be both creator and updater of several A's. A must have a B as creator, and may or may not have a B as updater. (Required and optional relationships.)

B might have both created and updated zero or more A's.

With inverse relationships

The above is the data model I built. (I don't really need the inverse relationship from B to A, but CoreData gives me compile time warnings otherwise.)

EDIT

Here are the relevant parts of the ManagedObject subclasses:

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]; }

END EDIT

When I create a new A with a B as creator, everything is fine (when using inverse relationships). However, when I update that same A later, using the same B as creator, i get the following crash:

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

(Yes, B has the method addUpdatedObjectdefined.)

For further background, the list of A:s are shown in a UITableView backed by a NSFetchedResultsController.

The strangest thing is, IMHO, that it's perfectly possible to add a new A (with B as creator) with the inverse relationship without it leading to a crash, while adding the same B as updater to that same A leads to a crash.

However, when I remove the inverse relationships, everything works as expected.

Without inverse relationships

I can add the same B as A:s creator and updater without any problems, and the app works as it should, except for the build-time warnings from CoreData:

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

So, is there any CoreData guru out there that can explain why the first case breaks?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

So要识趣 2024-11-12 13:13:22

你不应该能够建立双重关系。 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 添加属性即可实体。

You're not supposed to be able to set up a double relationship. Xcode 3.x and earlier wouldn't even allow you to create a model with double relationships (Just checking now with Xcode 4, looks like it will let you do so without warning. Not a good idea.)

Double relationships are strongly not recommended because they poise serious risk to graph integrity e.g. delete rules, what happens if you have a rule of delete on one relationship and deny on the other? I think graphs with double relationships are just trainwrecks waiting to happen.

More importantly, I've never see a situation in which such a data model was truly needed. Usually, a better data model design will obviate the apparent need for such a dangerous setup. In this case, I think you need to insert entities to model the actual relationships themselves because the relationships actually have behaviors or sorts:

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
}

This also lets you easily model things like the date of an update just by adding an attribute to the Update entity.

北座城市 2024-11-12 13:13:22

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

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

Did you create the ManagedObject subclasses again for both entities after changing the model (by adding the inverses)?

If you didn't, the model may try to add inverse references to an object which is not prepared to accept them.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文