th-in-gs/YACYAML — pull request #11 by alunbestor (2014-05-19 13:08:36+00:00)
Title: Added support for merging from custom mapping types.

Further to issue #10, this patch adds support for merging keys from maps that have been given custom types. It does so by annotating the custom objects with a map of the keys that were used to create them: this map is then retrieved and used when the object is merged. I've included a unit test of this functionality.

The annotation is done in `YACYAMLUnarchivingObject.m`, in two possible ways:
- **If the object being unarchived implements `YACYAMLUnarchiveMapping:`** an `NSMutableDictionary` is built up alongside the unarchived object and is populated with the same keys and values from the mapping. The object is then annotated with this dictionary once all keys have been processed.
- **If the object being unarchived implements `NSCoding`:** the unarchiving process already builds up a dictionary to initialize the object with, so this is reused. The uninitialized object (before `-initWithCoder:`) is annotated with this dictionary in case of cyclic references, and then the finalized object (after `-initWithCoder:`) is re-annotated in case the object is replaced.

The annotation is retained, and will get cleared when the `YACYAMLUnarchivingObject` responsible for the object gets deallocated (which would normally occur when its parent `YACYAMLUnarchiver` is deallocated.) This should ensure that the dictionaries are only kept around for the duration of the document unarchiving process.

Only objects that do not descend from `NSDictionary` are annotated in this way, since the existing merge key support already handles dictionaries. As a further optimization, **only elements that have an anchor** get annotated, since otherwise they cannot be referred to in a merge. (Technically this prevents constructions like `<<: !CustomType { key: value }`, but that would be pointless and very unlikely to appear in the wild.)

A note on the unit test: this only tests merging using an `NSCoding`-based class, because I found I could not write a useful test for `YACYAMLUnarchiveMapping`-based classes. This is because *implementors of
`YACYAMLUnarchiveMapping` need to entirely reimplement merge key support themselves*, and YACYAML does not actually expose the `YACYAMLMergeKey` class for them to recognise merges.

Rather than require implementors to reimplement their own merge functionality, I think it would be better to move the merge handling out from the `NSDictionary` category and into `YACYAMLUnarchivingObject` so that it works out of the box with any mapped type.