I had a straight forward approach of turning Key/Value pairs of an XML excerpt into an NSDictionary when I started a simple iPhone application. The problem is, I need to turn those NSDictionary's instances that once populated my UITableView's into custom classes because they require behavior and additional complexity. The problem here is that now in order for me to instantiate an object and fill its instance variables with key/value pairs from a web service becomes that much more difficult. I can no longer throw it into a method that iterates through the XML and uses KVC to set its instance variables.
What kind of other solution is out there?
You can still use key value coding methods on your custom class, as long as you name your variables appropriately there's no difference there. With that being said though, when I'm working with XML I usually end up testing each node name or creating a key lookup table, since the names in the data source I'm working with aren't key value coding compliant. If you have control over the data source though, you could just continue to use setValue:forKey:.
I'd recommend reading this guide about key value coding if you haven't already. It's fundamental to many great tools in Cocoa.
Look into NSCoding. You can use the NSCoding protocol to save your object, properties and all, as data.
Once your object is NSCoding compliant, you can just archive the whole array of objects using NSKeyedArchiver.
Please note that if you have a large number of objects, this can dramatically affect the app's performance on the iPhone during load and save.
Related
I've been doing iOS now for about two years and mostly by consequence of simple data layers (no real need for ID's, joins, complex queries, etc), I've always managed persistence manually by extending NSObject, conforming to NSCoder, and saving my data in flat files.
However, now I am in a position where I need to start doing more complex query work and I am digging into CoreData.
My hangup right now is with NSManagedObject. It's a pretty intimidating class and I am wondering if there are any unwritten gotchas I should be aware of that might restrict my freedom while building a sophisticated application.
For example, things like #dynamic getter/setters are not to clear to me, etc
So:
1) Are there any features of NSManagedObject that would prevent me from handling instances of custom classes just as I would with NSObject?
2) Does NSManagedObject have any unusual behavior when objects are handled WITHOUT the presence of a ManagedObjectContext?
3) Can I supplant a #dynamic property declaration with my own custom getter/setter without too much pain? I use a lot of custom setters to enforce business logic.
and so on, and so on.
Thanks
Beware!
Core Data is not lightweight. It will require a lot of changes to the way your objects work, unless they are very basic.
1) Are there any features of NSManagedObject that would prevent me from handling instances of custom classes just as I would with NSObject?
Faulting and the lifecycle of managed objects are a little tricky to understand. Your objects can be "faulted" almost at any time (each iteration of the event loop anyway), which makes them lose any non-persistent state. When they get "un-faulted", they don't have access to anything outside the managed object context to restore their state. It's very hard for managed objects to access anything outside the managed object context.
Transient properties are possible, but are pretty much limited to things that can be computed from the persistent properties, since their state will be lost if the object is ever faulted. You also have to formally declare that you're using a transient property in the data model. Trying to just set up instance variables will run into problems.
So basically managed objects properties all must be persistent or related to persistent properties. Look at the objects you're planning to make managed. Are all their instance variables feasible and desirable to put into a persistent store? If not things will not be straightforward. You probably set up your instance variables in your init. There is no init that gets called every time your program runs for managed objects. There are awakeFromInsert and awakeFromFetch, but those don't work exactly like init. Your code calls init and can pass arguments. The framework calls awake and it gets no parameters.
Threading is another issue to consider. A managed object context, and therefore the managed objects in it, are recommended to be used only by a single thread. If you have multiple threads, and they need to access your model, you need to create duplicate managed object contexts containing duplicates of your managed objects. Core Data is designed to handle that without putting too much strain on memory, disk I/O, and processor, but it isn't lightweight from a coding perspective.
2) Does NSManagedObject have any unusual behavior when objects are handled WITHOUT the presence of a ManagedObjectContext?
You cannot have a managed object without a managed object context.
That's not usually an issue. Managed objects carry a pointer to their managed object context. It can be a little limiting though. If something outside your model needs to create a model object as an input to your model, it needs to have access to the managed object context to do that. Hopefully that would be through some kind of abstraction layer so it doesn't need to know it's dealing with Core Data, but you can't just give it a class and have it alloc/init. It is also a bit of work to move managed objects between different managed object contexts.
3) Can I supplant a #dynamic property declaration with my own custom getter/setter without too much pain? I use a lot of custom setters to enforce business logic.
Yes. It's a bit complicated but doable. You write your own accessors. In addition to your business logic, they must call key-value observing methods like willChangeValueForKey:. Then you access the data using primitive accessors.
1) Are there any features of NSManagedObject that would prevent me from handling instances of custom classes just as I would with NSObject?
Creating and removing them is different, but not difficult. Otherwise, no
2) Does NSManagedObject have any unusual behavior when objects are handled WITHOUT the presence of a ManagedObjectContext?
I'm not sure what you mean, you can pass a managed object to another class and use it quite happily without the other class knowing about the context, but you must have a context somewhere.
3) Can I supplant a #dynamic property declaration with my own custom getter/setter without too much pain? I use a lot of custom setters to enforce business logic.
Yes, NSManagedObject is designed for this purpose. There are plenty of examples of overridden accessors in the core data programming guide - there are some rules you need to follow but they are not too onerous.
Core data is primarily a persistence framework rather than a query framework - I imagine once you get into it you will wonder why you were wasting your time on all those NSCoding implementations...
1) Are there any features of NSManagedObject that would prevent me
from handling instances of custom classes just as I would with
NSObject?
No. You can extend a NSManagedObject subclass just like a NSObject subclass.
2) Does NSManagedObject have any unusual behavior when objects are
handled WITHOUT the presence of a ManagedObjectContext?
The #dynamic directive will create accessors optimized for saving in a persistent store managed by a context. The values will still be available just like any other object but you will generate a lot of overhead.
3) Can I supplant a #dynamic property declaration with my own custom
getter/setter without too much pain? I use a lot of custom setters to
enforce business logic.
You can but you will incur some performance penalty because the generated accessors will be highly optimized for the specific code in your app.
One major point of novice confusion with regard to managed object is missing that the generic NSManagedObject can represent any entity in the data model without subclassing. NSManagedObject has "associative storage" which basically means that it has an open dictionary attached to it that save any key-value pairs. When you you insert a generic NSManagedObject into a context, the keys of the associative storage "dictionary" are set to the property names of the entity given.
Therefore, you are actually never really forced to subclass NSManagedObject. Doing so is largely a matter of convenience.
That is why you sometimes see code that uses generic NSManagedObject and the setValue:forKey and other times you see custom subclasses that use anObject.propertyName.
I would also advise that if you find yourself using or saving managed object outside a context, then chances are good that you have a bad design.
Any advice on how to store a simple custom config class in core data. Options would be I think:
Entity with a "key" and "value" attributes - perhaps different attributes to represent different possible types (e.g. Integer16, String etc)
Custom class type entity which specific attributes for each config item I want - only catch would be that you would only ever want have one record in this entity, but the benefits would be it should be more usable I think: e.g. for the "fontSize" config items it would just be once you get the 1st record back from core data: "configRecord.fontSize". No need to access via a key-value arrangement and then cast the result or whatever.
Comments?
If there would only ever be one of these, I am not sure why you would save this information in Core Data and not simply as NSUserDefaults. Don't get me wrong, I like Core Data a lot. But seems like a lot of overhead for what could be stored as a dictionary.
If you had many of these, then Core Data and your second option would enable easy searching, etc. But this is not the case.
In terms of "configRecord.fontSize" convenience, you could read in NSUserDefaults into your own custom class and provide getters/setters there - without resorting to Core Data.
Just my two cents worth ;-)
I've run with my options 2 which seems to work a treat - and lieu of any suggested disadvantages (which I haven't found yet)
I have a list of objects that can sometimes change, and I want to keep a persistent cache on the device whenever the app is closed or move to the background.
Most of the objects in the list will not change, so i was wondering what is the best way to save the list. I have two major options i think about:
Using NSKeyedArchiver / unArchiver - This is the most convenient method, because the objects i'm serializing hold other custom objects, so this way i can just write a custom encode method for each of them. The major problem is that i didn't find on Google how to serialize only the changed objects, and serializing the entire list every time seems very wasteful.
Using SQLite - this is what i'm currently using, and the worst problem here is that adding \ changing properties of the objects is very complicated, and much less elegant.
Is there any way that i can enjoy the convenience of NSKeyedArchiver but only serialize the changed objects?
Like Adam Ko I would suggest using Core Data:
This kind of problem is what it's really good at, after all!
If your cache items are independent from each other, this could be achieved by simply wrapping your cache-items by a thin layer of NSManagedObject (i.e. you could benefit from Core Data with only minor changes to your app).
This wrapper entity could store an archived version of a cache item in an attribute of type NSBinaryDataAttributeType and provide access to the unarchived object through a transient property.
See Non-Standard Persistent Attributes for an example.
I have a few Moose objects and some other simple hash objects (hashes, arrays) I'd like to serialize.
At first, I used a simple
my $obj_store_file = nstore($obj);
and
my $obj = retrieve($obj_store_file);
This worked well.
Later, I found about MooseX::Storage and KiokuDB. I tried using them to enjoy some benefits they have, but:
MooseX::Storage seemed to recreate objects that are referred multiple times. For example, one of my serialized objects contains a few attributes, which each of them refers to the same instance of another object. Before serialization, all of these reference are obviously the same -- they all point to the same object. After serialization/de--serialization using MooseX::Storage, this once single object is duplicated and each reference points to another instance of the object. I was told that MooseX::Storage is not appropriate to represent object graphs and that I might want to try KiokuDB.
I did, although I felt KiokuDB is an overkill for my needs. I don't need all the fancy stuff a DB can offer. Unfortunately, since one of my objects is really large and choaks on memory when serialized using defaults, it seems I have to write a custom serializer or store its 'data' portion separately then write a costume KiokuX::Module... again, quite an overkill.
So, I'm back to plain ol' Storable or YAML. My question is simple: yes, there are some benefits for KiokuDB (especially the fact it maintains an object graph) and perhaps also for MooseX::Storage (although I couldn't really find any for the latter). But, given these benefits are not really of use to me, is there any reason not to use Storable or YAML?
In other words, is there anything wrong with storing a (Moose) object this way? Is it 'illegal'?
My experience is that it depends on why you're serializing data. I like Storable for program state including things like window size/position. I prefer YAML for configuration data or anything you might want to exchange with another copy of the application. (i.e. share between users -- a Storable file might not be readable by a user with a different version of Perl or Storable.) Storable supports object graphs (assuming that the freeze/thaw is done correctly). I'm not sure about YAML.
I'm writing an App that basically uses 5 business entities, A, B C, D and E
A has some properties and holds a list of B's
B has some other properties and a list of C's and a list of D's
C has some other properties and a list of D's and a list of E's
D has only a few properties
E has only a few properties
There is no inheritance between any of them.
There's no real business logic involved, the objects are created, populated, and then accessed read-only, no further manipulations.
My natural coding style would be to go object oriented and write classes for each of those entities, use NSArrays for the lists, and have the mentioned properties synthesized.
It would make the code readable.
But another approach seems obvious too: only use NSDictionaries and NSArrays, and working with keys/values instead of properties. This seems more efficient, and somehow "closer" to iPhone-style programming to me... but obviously leads to less readable code. Another advantage is there's no additional custom encoding/decoding for serialization required (persisting state to disk, using JSON, ...)
So on the paper, it speaks for the latter approach, on the other hand, it still feels somehow awkward NOT to use custom objects...
Is this really just a matter of taste question? Or are there maybe other arguments in favour/against one of the approaches? Is only using Dictionaries better memory/performance-wise? Is it the preferred "Apple Coding Style"? (I'm coming from Java/C#).
I don't see much difference between Java/C# and Cocoa in this area. Your question is equivalently applicable to those platforms as well (the same also applies to key-value stores and relational stores).
In an object oriented environment, you have to make a trade-off between the flexibility of the key-value approach for storing data and the structured and object oriented style. I'd go with the key-value approach only when I need the flexibility (e.g. the structure is dynamic and might change by user or not known at compile time). Otherwise, taking that route might get you completely off the OOP conventions and benefits (By the way, this is the important point. Does the hassle of sticking to object oriented principles worth it for that specific circumstance? I think your question reduces to this one and to answer it, you should analyze your specific situation)
It largely depends on whether your objects are just collections of data (key/value pairs) or implement their own functionality.
If they're data I'd say go with NSDictionary, it's a lot less code and as you point out you won't have to write serialization routines for each class.
Use a hybrid approach. Store the dictionaries the objects are based on, but expose the most-used values as properties that are either filled when the object is initialized from a dictionary, or have the accessors look into the dictionary for values (less efficient).
Also provide a property to get at the dictionary. This way if you need to propagate a new value quickly to a specific area of the code from the dictionary (presumably a new value added by the server) you have that flexibility. Then if callers are making heavy use of a value you can migrate it to be a true property and get the completion and type checking of a property.