I am unclear what's the most appropriate way to represent simple data objects with properties in cocoa.
In Java, java beans make a lot of sense to implement such objects: declare all the properties of your object and create getters and setters for each of these properties.
In cocoa, we can follow the same approach. But you can also just use an NSDictionary. Which approach makes most sense when?
Taking a concrete example: what's the most "appropriate" way of representing vehicles that have, say, two properties: number of axles, and color? Do you create a Vehicle class that subclasses NSObject and that has two properties numberOfAxles and color? Do you create a subclass of NSDictionary with two keys?
The former approach seems cleaner to me but when I look at code samples from Apple, (e.g. UICatalog), I see arrays of dictionaries everywhere to represent data sources. Seems odd to me. Is is just because NSDictionary offers a quick and dirty way of representing data objects, which is practical in the context of simple, self-contained examples?
If you want to simply represent a vehicle (with no future extensions,etc and no functionality) then using a dictionary should be fine. (Although, I would use a Vehicle class)
Also, apple's examples are to show APIs/SDK usage only. In 99% of the cases they don't aim to teach design patterns or objective-c language.
It mostly depends on what you prefer. If you're just storing simple data structures, an NSDictionary is fine. If you want to add logic to your data structures, use a class.
Related
All those “NSOrderedSet was added later and thus doesn’t have to play nice with other components” bugs drive me crazy…
(https://twitter.com/kubanekl/status/413447039855640576)
I have two managed objects and an ordered 1:N relationship between them, which is backed by an instance (or more precise a subclass) of NSOrderedSet. I want to manage this relationship using a NSArrayController in order to profit from features it offers (selection management, binding of the content, bindings to views like NSTableView).
Since NSOrderedSet is not a subclass of NSSet, the contentSet binding of NSArrayController doesn't work with that relationship. I found following thread and tried to implement the suggestions mentioned there.
The first suggestion is to use the contentArray binding and a value transformer for transforming the ordered set to an array on the fly. The problem with this solution is the reassigning of the content each time a change is made, which is not what I want.
The second suggestion provided in the mentioned thread is to use the contentArray binding and apply the #array operator to the model key path. I've tried that, but the underlying relationship was not touched at all when adding/removing objects through the NSArrayController.
Another option I found is using sort descriptors with the contentSet binding. This would require making the relation unordered in order to make the contentSet binding work and introducing a new attribute used especially for managing the order. This would furthermore require a custom ordering mechanism to implement and it would mess up the model. Honestly said, I would like to avoid this solution.
My question is pretty clear: Is there any way to manage an ordered Core Data relationship using NSArrayController? If so, which is the best way causing as little pain as possible?
It is indeed very sad that NSArrayController does not come with support for ordered relationships. As a keen observer to the bindings technology I find it sub optimal that it seems that Apple has "abandoned" it without saying anything. The last notable change that Apple introduced with regards to bindings are NSTreeController bug fixes. That was I believe with 10.6/10.7. It seems that Apple does not want to touch the bindings technology anymore. I am not sure why because bindings are really great sometimes. They can be the "90% solution". During prototyping this is fine. I am using bindings where it makes sense and having NSArrayController with ordered relationships support would be something great.
Most of the solutions that have been mentioned already are no real solutions. But this depends. Here is something to think about:
If you are planning to support iCloud then you should not/cannot use ordered relationships anyway because Core Data on iCloud does not support them.
Since ordered relationships are fairly new and the desire for a ordered collection of objects existed long before them, there must be a way in Core Data to mimic ordered relationships. You have already pointed out what 99.9% of the Core Data eating world did before ordered relationships were available: Sort by an additional attribute. You have pointed out that this is messing up the model but I disagree: It is true that you have to add an additional attribute to your model which does not necessarily "represent" true model data. But how many ordered relationships are you planning to have in your model? Usually you don't have that many per application. Even though it feels a bit dirty this is what has been done by lots of people for at least three major releases of Core Data (10.4, 10.5 and 10.6). Even today this solution is used for backwards compatibility or if you want to use iCloud. It is a "pragmatic" solution. Not a nice one but pragmatic. Also please not: Even if you were using ordered relationships the order of your objects has to be stored somewhere. If you are using the SQLite store then having an ordered relationship causes the NSSQLiteStore to create an additional column for you. The column has the name: Z_FOK_$RELATIONSHIPNAME. So by using ordered relationships you are merely doing something that is done for you under the hood anyways. This means that it does not really matter from a pure technical perspective if you are using ordered relationships or an additional attribute. The underlying technical problems remain the same. Ordered relationships are no magic.
If you are planning to go with the "additional attribute" solution be aware that you have to adjust the value of this attribute a lot: Every time the user is changing the ordere via drag and drop you have to modify the value of the attribute. This seems wasteful but it really isn't. Even the worse case: A user who is exchanging the object at row 0 with an object at the last possible row does only cause 2 attribute changes. The complexity of the trivial solution for changes needed to represent any change that can be made by drag and drop in a table view is O(n) where n is the number of selected rows. This is really not that bad since users are usually not reordering 10000000 rows at once and even then there are smarter algorithms out there which are not that hard to implement.
If you are looking the for cleanest solution you should subclass NSArrayController and add a "orderedContentSet" bindings yourself. You can find out how to do that by reading the Cocoa Bindings Programming topic guide. The guide contains an example: https://developer.apple.com/library/mac/documentation/cocoa/conceptual/CocoaBindings/Concepts/HowDoBindingsWork.html (Listing 2). The bad thing about this is that you are subclassing NSArrayController which is usually a no go. Many people tend to subclass NSArrayController for reasons that don't justify subclassing it. In this case however subclassing NSArrayController is justified if you want to go with the cleanest solution.
For 3. there are generic solutions out there which do a lot of the stuff for you. Don't use them.
I have a custom class that exposes an NSString property. In Interface Builder I've bound the title of an NSButton to the property of my custom class.
Is it possible to get a reference to the NSButton instance from within my custom class?
Essentially I'm trying to locate all the user interface elements that are bound to the property in my custom class.
In general, this sounds like an anti-pattern and/or a bad idea. That said, there are a couple of things to bear in mind. Multiple observers could be bound to your property. You can override addObserver:forKeyPath:options:context: and removeObserver:forKeyPath: (and removeObserver:forKeyPath:context:) and then maintain your own array of observers. With that approach I would caution you that you may need to go to extra effort for the array to not retain observers, as traditionally KV observations don't retain the observing object, and you will likely run into leaks/heap growth if you start retaining them by putting them in an NSArray.
The other gotcha with overriding addObserver:... and removeObserver:... is that, without considerable extra work, you wont know if the observation is for a binding or for something else (like, say, a dependent keyPath notification). One possible workaround for that would be to interrogate the observer via infoForBinding: on all exposedBindings on a later runloop pass using performSelector:afterDelay:. (I think I just threw up in my mouth a little bit for suggesting this.)
Relying on private implementation details of the KVO system is not likely to be a good approach, unless your goal is simply to better understand how KVO works, but it sounds like you're actually trying to accomplish something.
Really, this whole approach just feels like a recipe for disaster. It sounds like an MVC violation from the get-go. Why would the model object need to know about the view objects? Whatever you're trying to accomplish here would almost certainly be better accomplished by having the nib be owned by an NSViewController subclass which has IBOutlets for all the UI elements, and properties for the model. That object would then be in a position to more cleanly manage the apparently complex relationship between your view and model objects without runtime trickery. Since you've not elaborated on the ultimate goal of this trickery, it's hard to say what the best approach would be.
Say I'd like a mutable, unordered to-many relationship. For internal optimization reasons, it'd be best to store this in an NSMutableDictionary rather than an NSMutableSet. But I'd like to keep that implementation detail private.
I'd also like to provide some KVO-compliant accessors, so:
- (NSSet*)things;
- (NSUInteger)countOfThings;
- (void)addThings:(NSSet*)someThings;
- (void)removeThings:(NSSet*)someThings;
Now, it'd be convenient and less evil to provide accessors (private ones, of course, in my implementation file) for the dictionary as well, so:
#interface MYClassWithThings ()
#property (retain) NSMutableDictionary* keyedThings;
#end
This seems good to me! I can use accessors to mess with my keyedThings within the class, but other objects think they're dealing with a mutable, unordered (, unkeyed!) to-many relationship.
I'm concerned that several things I'm doing may be "evil" though, according to good style and Apple approval and whatnot. Have I done anything evil here? (For example, is it wrong not to provide setThings, since the things property is supposedly mutable?)
I wouldn't make a property (even a private one) for the dictionary, but I don't think there's anything wrong with it.
… is it wrong not to provide setThings, since the things property is supposedly mutable?
Yes. KVC will not like the absence of a setThings: method.
Nothing evil here. The only mandatory mutation methods for an unordered relationship are addThings: and removeThings: (see the KVC doc). The accessors for your keyedThings property won't collide with any KVC accessor, so you're also fine there.
To put your mind at ease, the only things Apple's static analyzer is known to check for are messages to undocumented APIs. Other than that, if your implementation decisions don't affect the app's behavior, you're alright for App Store approval.
Update: I got interested in this question and re-read the KVC doc for myself. The language here gave me pause:
To-many unordered relationships are
most often modeled using instance of
NSSet or a subclass. In that case the
key-value coding will, if it doesn’t
find these accessor patterns for the
property, directly access the set.
Typically, you only implement these
methods if you are using a custom
collection class that needs to be
accessed as if it was a set.
It sounds like the author would prefer that you get rid of things and implement enumeratorOfThings: and memberOfThings:.
Why does cocoa use delegates rather than inheritance?
With delegates, you can have one object be the delegate of many other objects. For example, you can have your MyController instance be the delegate of an NSTableView, an NSTextField, an NSWindow, and any other objects that compose your interface. This gives a compact place to put all of your user interface code related to one section of your UI.
If you'd done that with subclassing, you'd have to create one subclass every object you wanted callbacks from.
Also, this is a classic inheritance vs composition question
In general creating a subclass can be a time consuming process, requiring a lot of groundwork, and overriding various template methods.
Meanwhile, using a delegate allows you to create a simple object that answers a few specific question or reacts in various ways.
Now when you combine this with the dynamism that you can achieve by swapping out delegates on the fly it can create a very flexible robust system that promotes more code reuse.
There is some general discussions regarding these things here and here. You can also find some older SO questions here and here.
Discussed at length here:
http://www.cocoadev.com/index.pl?ExtendsIsEvil
And Java guys know this too:
http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html
I'm trying to figure out how to decide when to use NSDictionary or NSCoder/NSCoding?
It seems that for general property lists and such that NSDictionary is the easy way to go that generates XML files that are easily editable outside of the application.
When dealing with custom classes that holds data or possibly other custom classes nested inside, it seems like NSCoder/NSCoding would be the better route since it will step through all the contained object classes and encode them as well when an archive command is used.
NSDictionary seems like it would take more work to get all the properties or data characteristics to a single level to be able to save it, where as NSCoder/NSCoding would automatically encode nested custom classes that implement the NSCoding interface.
Outside of it being binary data and not editable outside of your application is there a real reason to use one over the other? And along those lines is there an indicator of which way you should lean between the two? Am I missing something obvious?
Apple's documentation on object graphs has this to say:
Mac OS X serializations store a simple hierarchy of value objects, such as dictionaries, arrays, strings, and binary data. The serialization only preserves the values of the objects and their position in the hierarchy. Multiple references to the same value object might result in multiple objects when deserialized. The mutability of the objects is not maintained.
…
Mac OS X archives store an arbitrarily complex object graph. The archive preserves the identity of every object in the graph and all the relationships it has with all the other objects in the graph. When unarchived, the rebuilt object graph should, with few exceptions, be an exact copy of the original object graph.
The way I interpret this is that, if you want to store simple values, serialization (using an NSDictionary, for example) is a fine way to go. If you want to store an object graph of arbitrary types, with uniqueness and mutability preserved, using archives (with NSCoder, for example) is your best bet.
You may also want to read Apple's Archives and Serializations Programming Guide for Cocoa, of which the aforelinked page on object graphs is a part, as it covers this topic well.
I am NOT a big fan of using NSCoding/NSCoder/NSArchiver (we need to pick a name!) to serialise an object graph to a file.
Archives created in this way are incredibly fragile. If you save an object of class Foo then by golly you need to make sure when you load the data back in you have a class Foo in your application.
This makes NSCoder based serialisation difficult from the perspective of sharing files with other applications or even forwards compatibility with your future application.
I forgot to list what I would recommend.
NSCoding can be ok in certain situations: if you're just doing something quick and simple (although you do have to write a lot of code - two methods per class to be serialised). It can also be ok if you're not worried about compatibility with other applications.
Export/import via property lists (perhaps using the NSPropertyListSerializaion class) is a fine solution. XML based plists are easy to create and edit. Main advantage to plists is that you're not tying the file format to just your application.
You can also create your own XML based file format and read/write to it using NSXMLDocument API and friends. This really isn't much more work than using property lists.
I think you're a bit confused, NSDictionary is a data structure, it also happens to implement the NSCoding protocol. So in essence, you could either put all your data into a NSDictionary and have that encode itself later on, or you can implement the NSCoding protocol and encode your object tree using the NSCoder API. Based on the type of NSCoder object passed in to the encodeWithCoder: method, is the output of your encoding.