NSCoder vs NSDictionary, when do you use what? - cocoa

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.

Related

TreeView without duplicate all data to model

I have a huge amount of data, already stored in STL containers. It feels very bad to do full copy for all data to Gtk::ListStore. And also, my data structure contains big gaps in some rows, which simply can be filled with a default value if the line of the view becomes visible. To store tons of default values into a model is also a bad overhead.
For this I thought it is easy to setup a own model which will then provide some entry points/callbacks where I simply can provide my own data from my containers.
But I can not find any line of documentation.
Here a standard TreeView, but no idea how to improve with own model:
https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-examples.html.en
In https://developer.gnome.org/gtkmm-tutorial/stable/sec-treeview-model.html.en I found the beautiful sentence
Although you can theoretically implement your own Model,
you will normally use either the ListStore or TreeStore model classes.
After that I take a look into the sources... well, because Gtkmm is only a wrapper over the C-code, it is not simply deriving from a class and overwrite some methods. It is more to pick up the underlaying c code if my interpretation of the code I saw is correct.
Anyway, is there any chance to get some lines of code where the ListStore is replaced by a own model?

What features does mogenerator provide?

I've been using mogenerator for a while now, and while there is a reasonable Getting Started Guide and a Stack Exchange article on the command line options, I haven't found a good guide for all of the functionality it provides.
In short: what, above and beyond the classes that Core Data provides for you, what does mogenerator actually generate?
(Frankly, I kept finding little pleasant surprises in the headers/implementations that I didn't realize were in there and I decided to step through the mogenerator templates and code and document what I found in a Stack Exchange Q&A. I'd love to see additional answers and edits, however. )
In addition to its core feature of a two class system, mogenerator helps you by automatically implementing a number of best practices regarding Core Data in your machine header and implementation files.
Property Accessors
Methods to access the attributes of your Entities are the core of what mogenerator generates. But there are some nice features implemented in the accessors above and beyond what the out of the box Xcode class generator provides to you.
Scalar Accessors
Xcode's built in generator gives you the option of "use scalar properties for primitive data types". This option gives you choice of having Xcode create properties with NSTimeIntervals instead of NSDates for date types, BOOLs instead of NSNumbers for boolean types, and int16_t (or similar) rather than NSNumbers.
I find this infuriating because most of the time I prefer the primitive types, but not for NSDates which are much more useful than a NSTimeInterval. So Core Data is giving me the choice of objects, in which case I will be constantly unboxing stuff and making stupid mistakes like if(myBooleanAttribute) (which is always YES because myBooleanAttribute is a NSNumber, not a BOOL). Or I can have scalars, but in that case, I get NSTimeIntervals that I'll always have to convert to NSDates. Or I can hand edit all of the generated files by hand to give me my desired mix of NSDates and BOOLs.
On the other hand, mogenerator provides you with both options. For example, you will get both a myBooleanAttribute getter that gives you an NSNumber (for easy storage in an NSArray) and a myBooleanAttributeValue getter that gives you an actual BOOL. Same with integers and floats. (Mogenerator does not generate NSTimeInterval accessors: only NSDates.)
Typed Transformable Properties
If you have a transformable property, you can set a specific UserInfo key ( attributeValueClassName ) in the attribute that will specify the class that your property will return/accept. (And it will properly forward declare the class etc.) The only place I found this documented was on Verious.
In contrast, the Xcode code generator will only type these transformable attributes as id types.
Validation Declaration
While mogenerator does not automatically generate any validation methods, it does include the proper signature as a comment in the machine h file. The seems to largely be for historical reasons, but it does mean that it is easy to copy and paste the signature if you decide to implement it in your human file implementation. (I wouldn't actually uncomment the declaration as you aren't supposed to call validation directly.)
Primitive Accessors
Core Data already provides you these accessors to the primitive values, but for some reason doesn't include them in its Xcode generated headers. Having mogenerator include them in its header files makes it much easier to access a primitive value.
Fetched Properties
mogenerator will generate accessors for fetched properties. As far as I can tell there is no way to have the Xcode generator do this.
Helper methods
Automatic NSFetchedResultsController generation
If you have a to many relationship in your Entity and you pass --template-var frc=true into mogenerator, mogenerator will automatically generate a method to create a fetch request for the child objects associated with a parent object. It even automatically generates a unique cache name, and isolates everything inside an #if TARGET_OS_IPHONE preprocessor macro.
Even if this doesn't fit your particular needs, it is a great example of how the templates can be extended.
+fetchMyFetchRequest:moc_
If you like defining your fetch requests in the model, this is a lot better way to retrieve them than hardcoded strings.
-MyEntitySet
Mogenerator uses the magic of KVC to give you a NSMutableSet proxy into your relationships.
+entityName
Need to provide a entity name to a NSFetchRequest or other Core Data method? It's easy to avoid hard coded strings by using this simple method that returns the name of the entity as an NSString.
+insertInManagedObjectContext: and entityInManagedObjectContext:
Another way to avoid hardcoding entity names is to use these helper methods.
Typed object ids
Each of your headers and implementations also includes a MyEntityID class. They are empty interfaces and implementations that merely subclass the NSManagedObjectID class. Also, each model class has a helper method called objectID that overrides the standard objectID method in NSManagedObject. The helper method does nothing but cast the superclass's return value to the MyEntityID type.
The net result: the compiler can catch your mistakes if you ever accidentally interchange your object ids from different entities.
Miscellaneous
Subclassing a Custom Superclass
One of the command line options is --base-class: which allows you to specify a base class that all of your generated classes will inherit from. This is very useful, either so that you can have a base class where you define convenience methods (which, given Core Data, you probably should) or so you can use an off the shelf Core Data toolkit like SSDataKit (or both).
includem
A simple little thing, but if you specify a --includem argument, mogenerator will generate a header file that includes all of your model header files. Convenient if you want to include all of your headers in a PCH, or something some other standard header you include.
Const Definitions of All Attributes, Relationships, Fetched Properties
An extern declaration of a struct is included in the header that has an NSString defined for every attribute and relationship defined in your Entity. This allows you to define predicates and other parameters, without baking the names of your entities into your strings. For example,
req.predicate = [NSPredicate predicateWithFormat:
#"(%K == YES) AND (%K <= %#)",MyObject.favorite, MyObject.availableDate, [NSDate date]];
(This type of struct used for "namespaced" constants is described My Mike Ash on his blog
const Definitions of User Info Keys/Values
Similarly an extern declaration of a struct is defined in the header that includes the keys as members of the struct, and the values as a values. i.e.
NSLog(#"User info for key my key is %#",MyObjectInfo.mykey) //will log "myvalue"
Alternate Templates
One of the interesting things about mogenerator is that in building mogenerator its author (Wolf Rentzsch) has basically built a generic parser and templating engine for the xcdatamodel files produced by Xcode. So you don't need to use the mogenerator templates. You can provide your own with a simple command line argument. There are lots of user contributed templates on the GitHub site.
In fact, you don't even have to use Core Data. Many of the contributed templates allow you to generate a series of ordinary NSObject model classes based on the data model. (So called PONSOs: "plain old nsobjects"). Want to use the data modeler in Xcode, but some other persistence mechanism? mogenerator can help you there.
You don't even need to generate objects at all: another interesting submitted template just provides a diff of two different model versions.

How to parse the contents of a foreign file created with NSKeyedArchiver

I need to be able to compare two versions of a plist file created with NSKeyedArchiver. In particular, it's the "elements" file created in Xcode for a .xcdatamodeld file.
Since I have not created this file myself I can not recreate its object model. Instead, I need to understand which classes and keyed properties the archive contains.
Ideally, I want to create a tree containing strings representing the names of classes along with their property names and values. I assume that all this information is stored in the archive, so it should be possible to generically parse this archive, right?
I've read about NSKeyedUnarchiver and its delegate. I've only gotten as as as this:
Unarchive the file data:
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
unarchiver.delegate = self;
id graph = [unarchiver decodeObjectForKey:#"root"];
Here's the delegate method that gets called when an unknown class is requested. I return a proxy class here in which I hope to collect its values:
- (Class)unarchiver:(NSKeyedUnarchiver *)unarchiver cannotDecodeObjectOfClassName:(NSString *)name originalClasses:(NSArray *)classNames
{
NSLog(#"wants: %#", name);
return [ObjProxy class];
}
The proxy class implements
- (id)initWithCoder:(NSCoder *)aDecoder
in which I do not know how to proceed, not knowing the actual properties of the classes. NSCoder doesn't seem to provide any function to learn of the available keys. Is there a trick to get to them, maybe by overriding some of the lower level objc methods?
So far, with this little code shown above, when parsing the "elements" file, I only get the request for one class, "XDPMModel", and then it's done.
Any ideas how to get this working, i.e. traverse the tree deeper?
You can use PlistExplorer, it's a Cocoa Tool to inspect files written by NSKeyedArchiver.
I've done exactly this before, to decode objects stored in the preferences file for Panic's Coda. What I did was to use class-dump on the app (in your case Xcode and its related frameworks), which allows you to see the properties of the real objects, and use these to create proxy objects that match those properties. You can then use NSKeyedUnarchiver successfully and query the proxy objects for their values.
It's a fairly laborious process but it works perfectly. In my case I was working with a user defaults plist so I only had to define a couple of these proxy classes for the objects I was interested in, but I would imagine that you'll have to define quite a few for Xcode.
I'd also be very interested to know if there's a way to do this without having to go the class-dump route (possibly, as you say, via some of the lower-level Objective-C functions) because it would greatly simplify this type of reverse engineering.

Writing changes to a complex CFPreferences structure

I can easily see how to add hierarchical data to a plist file via the CFPreferences api.
However, whats far less obvious how to read from a CFPreferences a value stored inside a CFDictionary (that might be stored in turn, in a CFDictionary), and change it.
You can’t, you have to replace the root element. If this is too cumbersome, that’s a sign that you should be using model objects rather than collections and possibly move away from CFPreferences/NSUserDefaults to some other storage mechanism, perhaps Core Data.

Java bean in cocoa?

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.

Resources