I’m rather disappointed with my ability to write well defined objects leading to less erorr-prone code with CoreData, my problem being lack of control at time of instantiation. To me, if an object has relationships which should always be there, they should be established on object-init – I’ve always been a user of very specific inits and I don’t like the idea of reasonable defaults. A class ‘Car’ should not have a general init, it should be impossible to init without initWithEngine: if you’ve chosen to conceptually model your system so that there is no sensible default engine. In CoreData, I’m told that I’m supposed to never over-ride NSManagedObject’s initWithEntity:insertIntoManagedObjectContext: which would be the only way to ensure object sanity during all points during object lifetime. Instead they say do initialization with wakeFromInsert, but, since that’s not parameterized (obviously) you’re out of luck with getting the engine into the car in the above example. Instead you have to bend your design to work with the weak object sanity inherent in something modelled after a relational database instead of a true OO database, and open yourself up to programmatic errors by allowing a setEngine in, say, cases where the engine should never be changed. All in all it’s like CoreData forces you to be too lenient with object sanity. Anyone have any comments on this – am I off base and missing something? I’ve thought about overriding initWithEntity:insertIntoManagedObjectContext: but the documentation recommended not to and I like to follow framework-designers advice (having been a framework designer a lot myself and knowing they put a lot of thought into their recommendation).
Otherwise, to me it would seem the opposite: any custom class shoudl ALWAYS override initWithEntity:insertIntoManagedObjectContext: because, if nothing else, it introduces programmatic error to have to specify the entity, it should only be specified in one place so long as you aren’t making a subclass that handles multiple entities [which I can’t see the use for despite htem pointing out that that’s possible], in the subclasses initAndInsertIntoManagedObjectContext:… and in my case with the Car example it would then be initWithEngine:insertIntoManagedObjectContext:
Anyhow I feel like I’m in a straight-jacket here that keeps me from writing locked-down error-proof code that properly follows a well designed model. If anyone has any advice, it’d be appreciated… Otherwise I have some hard decisions ahead of whether to go with a clean design everywhere or to go with managed-persistence. (And the whole way undo/save seems to be dealt with is a bit restrictive too it seems, based on reading around on this Wiki).
(Disclaimer: I’m somewhat new to Objective-C but have a pretty hardcore OOA&D&P background, so some of this looseless might be sort of par-for-the-course with this language which does seem to keep it’s sanity more by stated-usage than programmtically-defined usage (like not allowing you to, in general, to disallow unparameterized init). But this seems to be the icing on the cake a bit. Maybe I’m paranoid but I don’t like anyone being able to even make a programmatic mistake, I don’t want people to rely on the documentation saying “call this init” – they should only be allowed to call that init, any other init should throw, or, if it’s a compiled language, fail at compile time… but, hey, this is a Cocoa not an ObjectiveC Wiki)…
Not to be rude but I think this is being a bit paranoid. I’ve always found it easier to fail gracefully than to never allow a mistake to be made but I can understand the drive. If you think NSManagedObjects are too restricting in their model, it may be best for you to use another data abstraction framework. Several exist and one may be more fitting for you. That said, NSManagedObject may still work.
The documentation for NSManagedObject merely says that you should use initWithEntity:insertIntoManagedObjectContext: instead of init. Fair enough. It appears to me that you could subclass NSManagedObject and rewrite initWithEntity:insertIntoManagedObjectContext: to throw an error. Be aware that this would likely break integration with some features of NSArrayController, especially the add: or newObject methods. You could then write your own initWithEngine: method and call super’s initWithEntity:insertIntoManagedObjectContext: (or, as I’ve found easier, NSEntityDescription’s insertNewObjectForEntityForName:inManagedObjectContext: method). Likewise, you’d have to add some checking for engines in Car’s setValue:forKey: and throw errors if the car was getting an engine it didn’t want.
Altogether, though, I think you’re missing the point of having a generic database abstraction. Asking it to be restricting runs counter its ability to be as all encompassing as possible. If this is private code or code that you have degree of management over, I’d expect the easiest way to go would be to add a designated initializer for your class and enforce its use. Otherwise, good luck writing error-proof code.
initWithEntity:insertIntoManagedObjectContext: is just the designated initializer for NSManagedObject. It’s the intializer that you should call from your own initializers. You’re free to provide a designated initializer for your own class and to write all your other initializers such that they call the designated one. Your designated initializer would then call initWithEntity:insertIntoMOC with the proper entity. That way, you can have as many different initializers with as many different parameters as you like, and yet they all will be certain to initialize the superclass (NSManagedObject) the same way. See file:///Developer/ADC%20Reference%20Library/documentation/Cocoa/Conceptual/ModelObjects/index.html for more details.
Can you provide a list of any of those data abstraction frameworks? I’d probably just grow-my-own serialization/undo if I don’t use CoreData but I’d like to take a look at what else is out there.
Thanks both… I understand and qualify that I may be a bit paranoid. I’m used to more restrictive, more statically defined systems (I’ll reveal I had a heavy upbringing in C++, which as we all know is quite a bit less dynamic than ObjC), so it’s hard getting used to systems that keep error-proof based on convention. Nonetheless, if I can’t even assert my convention, I’m sort of frustrated – both of you, though, have pointed out that it doesn’t say not to provide your own initializer, it just says not to over-ride theres. So my proposed alternative would not violate their conventions (because if this is all by convention, I don’t want to break one convention to assert another). Fair enough.
Breaking that particular entity being being used in an editable ArrayController is no problem, as, the whole point here, is that I want to have programmatic control over instantiation and there’s no reasonable default instantiation (in this case my actual class is a Transaction class which, at this stage in the design, should ONLY be created by a Treasurer:createTransactionTo:forAmount: method, and once created should never be modified – it would seriously, at this point [maybe transactions would be editable in later versions] violate all sorts of invariants I’ve established and be counter to the business model).
I’ll try this making-my-own-designator method out for fit – though now I’m thinking about writing the program concurrently in CoreData and without CoreData as a sort of case study comparing which ends up actually having a more manageable and self-documenting code-base. – HaneePatenaude