NSUserDefaults vs NSKeyedArchiver & NSCoder & Serialization - cocoa

According to Apple's documentation, Archives convert data into architecture-independent byte streams. What is the difference between serializing data (like saving values to a property list) and archiving it?
Can archives be created with or without NSCoder? Is the NSCoder protocol only used to archive custom objects?
I understand that archives are a way to save object graph relationships and maintain object mutability - am I correct in thinking that for saving an object graph with custom objects, I would need to encode those objects into NSData, archive them to a byte stream to maintain the graph relationship, and then have the option of saving to the defaults database with NSUserDefaults or saving to disk?
Additionally, what's the difference between the defaults database and saving to disk?
I just want to get a better understanding of how the terms all relate to one another.
I've laid out my general idea of how these things interrelate like so:
I THINK -
NSKeyedArchiver is used to encode and store an object graph as a byte stream. It traverses the object graph, maintaining relationships, and calls the encoding protocol methods on each object. NSKeyedArchiver just keeps track of the object graph and saves it - we can either save it to disk or we can save to NSUserDefaults (which is a property list).
*it is advisable to archive data to a file directory on disk instead of NSUserDefaults.
Objects can be serialized to a property list (or to NSUserDefaults) without encoding them as long as they are arrays, strings, integers, etc.
Let’s say we have an array (which is automatically NSKeyedArchiver and PropertyListSerializer compliant) and it’s full of custom objects. Those custom objects must implement the NSCoder protocol methods. Then, somewhere else, we can create an instance of NSData that is equal to an NSKeyedArchive using that array, and save that instance of NSData to our desired location.
What happens is, the archiver traverses over the contents of that array, implementing the NSCoder protocol methods that each object adheres to. It keeps track of relationships in the object (the array), persisting the object graph.
When we want that data back, we can go into the file we saved to, check its contents, and create an instance of NSData. We make that instance equal to the dataWithContentsOfFile of the path specified. Then, we create an array and unpackaged the NSData into that array by called the NSKeyedUnarchiver unarchiveObjectWithData method. The archiver traverses over each object in the NSData instance and calls initWithCoder, essentially deserializing the byte stream.
Places we can possible store objects -
NSUserDefaults
Disk (file path)
Ways to store custom objects -
NSKeyedArchiver, which implements NSCoder protocol methods to convert custom objects into a byte stream that represents an object graph.
What must we use to store custom objects? NSCoder protocol methods.

Related

Having an NSSet as part of an NSManagedObject in Core Data

I have a class which as its properties has a number of NSSet values. The content of these sets is not stored in Core Data, but comes from another source. They are basically ID strings which are unique. No point in filling up Core Data with them as separate entities.
However, as far as I can see it's not possible to store an NSSet as an attribute of an NSManagedObject. I guess I would need to serialise it into binary data by hand, whenever the object gets stored, and deserialise it when it gets retrieved from the persistent store? The same would also apply to storing other collection classes.
Has anybody else ever come across this issue and is able to give some advice?
You can set the attribute type to 'transformable' and then directly store the set into the attribute. Be sure that all of the contents of the set conform to < NSCoding >.
In Xcode, in the Project Navigator open up the project_name.xcdatamodeld source for the model editor and select the entity in question. Choose Editor in the Xcode menu bar and there is a Create NSManagedObject subclass... item. Make the subclass and use it for extra non-Core Data properties. The subclass becomes part of the model in place of the entity. It retains all of the entity's behaviors.

Clone structure of google protocol buffer messages to structure of builder objects

We use protocol buffers for storing data in a database (as blobs). At some point, we read them again, need to modify them and store them again.
The problem is, protocol buffer message objects are immutable. What we had in mind was just creating a new builder object using the protocol buffer message as prototype:
Foo.Builder.newBuilder(prototype)
This basically works. But as we have a nested structure, so the prototype object actually contains attributes which are messages themselves, this does not work. Excerpt from the documentation:
Since embedded message and string objects are immutable, they are shared between the original and the copy.
Is there a way of cloning a whole structure to new builder objects which are mutable?
Well, sort of. I've done this using DynamicMessage.Builder, Descriptor and FieldDescriptor. I recursively walk the object graph using a fully qualified name to the property I want to update. Once found, I update it and call build on the DyynamicMessage.Builder(s) back up the stack. It is not straight forward particularly when dealing with repeated fields.

Save IEnumerable in Isolated Storage

I used the isolated storage before to save text files, xml files and images. However, is it possible to save variables of type IEnumerable using IsolatedStorage or any other resource in windows phone 7??
Thanks,
You are misunderstanding core concepts.. There is no such thing as "saving variables", you save objects. Your variable points to an object, and that objects implements IEnumerable. Is On WP7, it is the object's actual class that determines whether that object can be serialized and stored on the ISO directly. If that actual collection class does not support serialization, you will have to re-wrap all its current elements into a List/Array/Dictionary/Stack/Queue - literally whatever what supports being serialized - and store that instead of.
Once you have an serializable collection, then your code for saving gets reduced to something as trivial as:
IsolatedStorageSettings.ApplicationSettings["blah"] = your_serializable_collection;
IsolatedStorageSettings.ApplicationSettings.Save();
and in general, that's it. Retrieving is similar:
var items = (SomeCollection)IsolatedStorageSettings.ApplicationSettings["blah"];
where SomeCollection may be an IEnumerable, a List/Array/Dictionary/Stack/Queue - whatever you had put there and whatever is implemented by the actual collection class.
If you want, you may use IsolatedStorageFile and write files directly, but unless you have a good reason to - there's no point in it, as using the common dictionary is far simplier.
In my other post you'll find some links:
How to do isolated storage in Wp7?
Use for saving/loading of data List which are serializable out of the box. Last time i tried deserialize an IEnumerable I got errors...

Cocoa's NSDictionary: why are keys copied?

All objects used as keys in NS(Mutable)Dictionaries must support the NSCopying protocol, and those objects are copied when they're used in the dictionary.
I frequently want to use heavier weight objects as keys, simply to map one object to another. What I really mean when I do that is effectively:
[dictionary setObject:someObject forKey:[NSValue valueWithPointer:keyObject]];
("When I come back and hand you this same key object instance again, get me that same value out.")
...which is exactly what I end up doing to get around this design sometimes. (Yes, I know about NSMapTable in desktop Cocoa; but e.g. iPhone doesn't support this.)
But what I don't really get is why copying the key is necessary or desirable in the first place. What does it buy the implementation or caller?
The copy ensures that the values used as keys don't change "underhand" while being used as keys. Consider the example of a mutable string:
NSMutableString* key = ...
NSMutableDictionary* dict = [[NSMutableDictionary alloc] init];
[dict setObject: ... forKey: key];
Let's assume that the dictionary did not copy the key, but instead just retained it. If now, at some later point, the original string is modified, then it is very likely that you are not going to find your stored value in the dictionary again even if you use the very same key object (i.e., the one key points to in the example above).
In order to protect yourself against such a mistake, the dictionary copies all keys.
Note, by the way, that it is simple enough to define -copyWithZone: as just doing return [self retain]. This is allowed and good code if your object is immutable, and the NSCopying contract is specifically designed such that the object returned has to be (sorta, kinda) immutable:
Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable.
(from NSCopying Reference)
and
The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.
(from -copyWithZone: Reference)
Even if your objects are not immutable, you might get away with that implementation if you only ever use identity-based equality/hash implementations, i.e., implementations which are not affected in any way by the object's internal state.
If you want to store pointers as keys then you'll need to wrap them in a NSValue object with +valueWithPointer:.
Since iOS 6 if you want to use pointers as keys, you can use the NSMapTable object, see http://nshipster.com/nshashtable-and-nsmaptable/
You can specify whether keys and/or values are stongly or weakly held:
NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory
valueOptions:NSMapTableWeakMemory];
Another option that could be appropriate sometimes is to use NSCache, which holds keys strongly and is actually thread-safe.

NSCoder vs NSDictionary, when do you use what?

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.

Resources