GrahamDennis / GDCoreDataConcurrencyDebugging

GDCoreDataConcurrencyDebugging helps you find cases where NSManagedObject's are being called on the wrong thread or dispatch queue.
MIT License
170 stars 23 forks source link

Incompatibility with object hierarchy #8

Closed pierrephi closed 10 years ago

pierrephi commented 10 years ago

I have been using this utility for a while now and recently I upgraded my data model to something a bit more complex with object inheritance

Lets say I have 2 base classes

IMIRegister and IMIRegisterItem both are abstract and IMIRegister references a collection of IMIRegisterItems (to many relationship "items") IMIRegisterItem has a reverse relationship "registre" and at some point I assign

IMIRegister aRegister; IMIRegisterItem aRegisterItem;

aRegisterItem.register = aRegister

in my code both aRegisterItem and aRegister are of the base class type but at runtime they are of a concrete type, it seems that class name fiddling performed by GDCoreDataConcurrencyDebugging prevents the objective C CoreData run-time to correctly recognise the concrete type instance as a valid pointer to the abstract type

2014-03-17 22:10:46.614 Kotoba![688:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "registre"; desired type = IMIRegister_GDCoreDataConcurrencyDebugging; given type = IMIHistoryRegister_GDCoreDataConcurrencyDebugging; value = <IMIHistoryRegister_GDCoreDataConcurrencyDebugging: 0x17dea6a0> (entity: IMIHistoryRegister; id: 0x17dd5e50 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIHistoryRegister/p50> ; data: {
    atime = "2014-03-17 21:10:16 +0000";
    ctime = "2013-08-19 14:56:07 +0000";
    items =     (
        "0x17f75770 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p4983>",
        "0x17f6e090 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p5007>",
        "0x17f77e90 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIConjugationItem/p5112>",
        "0x17f6dfe0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p4990>",
        "0x17f757a0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p4986>",
        "0x17f6dff0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p4991>",
        "0x17f6e0e0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIKanjiItem/p5015>",
        "0x17f6dfd0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionaryItem/p4988>",
        "0x17f5ebc0 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionarySearchItem/p4974>",
        "0x17f6e050 <x-coredata://D5693539-058C-4896-A231-E5B74A8A83C9/IMIDictionaryItem/p4997>",
        "(...and 39 more...)"
    );
    name = History;
    parentRegister = nil;
    position = 0;
    subRegisters = "<relationship fault: 0x17e07690 'subRegisters'>";
    type = 0;
}).'
*** First throw call stack:
(0x2e5baf03 0x38d4fce7 0x2e32eb3d 0x2e32d515 0x17d5f5 0x17d0a1 0x182def 0x186083 0x2e3929cd 0x2e392b13 0x185f97 0xe35bb 0x14ecdf 0x30f087f3 0x30fbacb3 0x30e69e09 0x30de2b57 0x2e586031 0x2e5839bf 0x2e583d0b 0x2e4ee7a9 0x2e4ee58b 0x3345b6d3 0x30e4d891 0xbc9c7 0x3924dab7)

If this is not clear I will try and make a small example to reproduce

GrahamDennis commented 10 years ago

Thanks for reporting this. The reason why this happens is that I dynamically subclass both IMIRegister and IMIHistoryRegister, and the subclassed IMIHistoryRegister doesn't inherit from the subclassed IMIRegister...

I believe the solution to this is to not do dynamic subclassing at all, but instead to use method-swizzling. This is a much more straight-forward approach and should hopefully vastly simplify the code, but will take a bit of work.

GrahamDennis commented 10 years ago

Turns out my idea won't work. I've pushed a partial solution that should solve your problem. It only works when CoreData entities inherit from abstract entities. You'll still get similar problems if you have concrete entities inheriting from concrete entities. I'll think more about how to solve that, but let me know if my fix solves your problem.

GrahamDennis commented 10 years ago

That should fix the problem properly. Let me know if you have any issues.

pierrephi commented 10 years ago

Thanks Graham, Good trick, unfortunately, that doesn't fix it for me because my base class is only abstract in principle since I am migrating from an older version of my data model and the class at the root of the hierarchy cannot be actually abstract, When it migrates the initial state of my store is only composed of abstract entities until I programmatically instantiate the derived classes. In my implementation I also have several levels of hierarchy and some levels are not abstract. I trust this will help others though

GrahamDennis commented 10 years ago

So the last version doesn't work? Commit 4995dbf was supposed to fix the problem for non-abstract entities too. If there's still a problem, let me know and I'll take another look.

pierrephi commented 10 years ago

I did give it a spin and got the same error, let me come up with a test case so you can reproduce first hand. The error has slightly changed, the desired type is still the modified type but the given type is now correct

2014-03-21 11:05:17.727 Kotoba![47139:60b] *\ Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for to-one relationship: property = "registre"; desired type = IMIRegister_GDCoreDataConcurrencyDebugging; given type = IMIHistoryRegister; value = <IMIHistoryRegister: 0xc1152f0> (entity: IMIHistoryRegister; id: 0xc1154f0 x-coredata://09A5AB63-7D2B-4DF8-88E4-5A4CBDB114F8/IMIHistoryRegister/p6 ; data: {

GrahamDennis commented 10 years ago

@pierrephi: This should now fix it. I've tested it myself. Please give it a try, if it works I'll push a new version.

pierrephi commented 10 years ago

Graham, superb! I have tested this and it seems to work perfectly Thank you!

GrahamDennis commented 10 years ago

Great!