Reproduce writeToURL/initWithContentsOfURL behavior with array of custom objects instead of dictionaries - objective-c

I'm replacing an array of NSMutableDictionary objects with an array of custom objects. Each has 15 or so instance variables which need to persist and a couple more which are transient.
I used to read and write these using -initWithContentsOfURL: and -writeToURL:.
What's the best way to produce the same plist file with the new data structure?
It seems like one approach for saving is to recreate an array of equivalent dictionaries and call -writeToURL:, and vice versa for loading. I wonder if there's a simple way to do that, or a simpler more direct approach.

I often find that the most expedient way to do this is to create a tree of NSMutableDictionaries from the tree of custom objects and archive it. Obviously this has memory and CPU overhead, and is not strictly as nice as implementing NSCoding as suggested by skidr0w, but as I said, it's sometimes quicker and easier.

You can implement the NSCoding protocol in your class, to archive a instance of your class on disk.

Related

What might be a suitable method to make deep copy of a Collection object in Objective-C?

The copy method only makes a shallow copy, i.e. a new copy of the collection itself, not all the objects storing inside. The book I am studying now suggested to use archive to create deep copy, i.e. use NSKeyedArchiver to save the collection object to an NSData object, then load it back to another collection object using NSKeyedUnarchiver. This method works, but I am not sure how efficient it is to cope with large collection objects. Is there any other deep copying method which might works better?
Alright, this is what I want to know:
Any method which involves less coding?
Any method which is more CPU efficient?
Any method which can achieve both? :P
What #dasblinkenlight said...
NSKeyedArchiver is designed especially for large object graphs. It is of course possible to build something more efficient if you have highly specialized knowledge of your object graph, but that would almost always be a mistake unless you have a very special problem.
Your book is correct. Use archivers to create deep copies.
I am not sure of the numbers, but the NSKeyedArchiver route is not going to be very computationally expensive. It looks like the long way round, but really is not much more inefficient than something custom made (like #Rob Napier said). It is, however a lot more reusable.
When still in doubt, an interesting experiment may be a deep copy of a large but simple data set, and time that against something custom made.

Custom Class vs. Dictionary vs. struct

My program should save, retrieve and manipulate a large load of data. (Around 200,000 entries, which all have around 20 entries.)
I'm wondering how I should implement the data: create my own class, use a struct or use NSDictionary.
The whole data would be saved in an array (for going through the whole data). But since performance matters (particularly the searching part) I would like to chose the fastest and savest way.
Any suggestions?
CoreData with sqlite store type is the way. You'd be able to fetch portions of data without reading the whole thing to the memory at once. This way you'd be able to generate custom NSManagedObject subclasses if you'd need custom behavior for each instance of an object.

Are objects added or replaced in a mutable array automatically persisted?

I am building a very simple app to store data on people like name, age, etc. I give the user the option of changing the data such as when a person has a birthday. I can't figure out the best way to store the data.
I have searched for a long time now and I can't find the simple answer that I am looking for. If I change the data in a mutable array will that data be changed permanently? I mean will that write over the existing code and change the data so that it will still exist when the app is closed and reloaded again?
So far I am not able to do that. I have experience in MySql and Sql server so I have a tendency to go that direction if possible, but I would really like to use a mutable array or mutable dictionary if possible. This may be such an elementary question, but I can't find an answer anywhere.
You have some misconceptions.
The objects you create are in memory. There's nothing permanent about them. You have to save them somehow or they are gone when you quit the application and come back.
If you want to save an array, you have a number of options.
If the array contains nothing but objects of type NSString, NSData, NSDate, NSNumber, NSArray, or NSDictionary, you can save the array using the system class NSUserDefaults.
NSArray also has a method writeToFile:atomically: that will save an array of data to a file.
If your array contains any objects other than the types I listed above, though, neither of those approaches (NSUserDefaults or writeToFile:atomically) won't work.
The other option is to use an NSKeyedArchiver to convert the contents of your array to data, and then write that data to a file. In order for that approach to work, every single object i your array, and all the objects in those objects, need to support the NSCoding protocol.
As others have pointed out, you could also use Core Data or mySQL to save your data, but that seems like overkill for just saving an array.
Take a look at Core Data. It is the easiest way to manage this kind of data storage requirement on iOS.
Take a look at my book, it got good reviews and is perfect for what you are trying to do: http://www.amazon.com/Pro-Core-Data-iOS-Professionals/dp/1430233559
If you want an easy way to get started, there are tons of online tutorials too. For example Ray has written some good stuff:
http://www.raywenderlich.com/934/core-data-on-ios-5-tutorial-getting-started
Mutable means you can change the data at any time, so no, it's not permanent.
Yes, it will be permanent in terms of what you are asking. Although because the array is mutable it means you can always change the data back, the initial data will be overwritten by this new data.
Edit: No, the information will not remain after closing the app. You will need to programmatically save it. I would use Core Data.
CoreData is the way data is normally persisted in Cocoa and Cocoa-Touch. It also gives you some nice extras like undo support. But it has a big learning curve.
If what you're doing is super-simple, look at NSUserDefaults. If you need a little more flexibility, you could always use NSArray's -writeToFile:atomically: and -initWithContentsOfFile: (there are also versions of both those methods that take URLs instead of file paths).
Anything more complicated than that, and it's probably worth the trouble learning CoreData.

Save MKOverlay objects in Core Data

I have a large MKOverlay that I would like to be saved in Core Data so that I don't have to create it later. Since this isn't one of the types that you can choose in Core Data, how do I go about saving it?
Do I need to somehow encode it first?
Do I then need to decode it when using?
What kind of object do I select in core data when creating a new property?
Thanks guys.
If you do not need to query for different overlays and you're not using core data elsewhere in your project, then you're probably better off caching the overlay on disk as an encoded NSArray.
However, if you're already using Core Data or you're caching multiple overlays then you can encode/decode the overlay in a field of type NSData. Add additional fields to the entity so you can query for the specific overlay you're looking for.
In iOS 5, you can enable optional storage of NSData fields in an external file by selecting the "Allows External Storage" option. Core Data will apply a size-based heuristic to determine if a blob or external file will result in better performance.
MKOverlay conforms to NSCoding, so you can encode and decode an entire array of MKOverlay objects using an encode method of NSKeyedArchiver and store the result in a binary field in your entity. You'll likely want + (NSData *)archivedDataWithRootObject:(id)rootObject on NSKeyedArchiver and + (id)unarchiveObjectWithData:(NSData *)data on NSKeyedUnarchiver
See the Archives section in the Archives and Serializations Programming guide for details of creating a keyed archive at: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Archiving/Articles/archives.html
You can write a custom accessor for the entity's binary field that encodes and decodes the overlay array for you. Another option is to create a value transformer that encapsulates the encoding and decoding operations. The end result would be an overlays array property that you can set and read via entity.overlays.
I believe you can use Apple's NSCoding libraries to convert the object to and from a serialized state. However, Core Data may support saving objects, but NSCoding lets you save any class that implements it anywhere, including a string sent to a server, a file written to disk, or if you're as bad a programmer as me, an NSUserDefaults entry.
edit- You may have to implement NSCoding into your own class based on MKOverlay by adding read and write methods, I'm uncertain.
Why not instead save the properties (size, color, coordinates, etc can all be described with NSNumbers and those can be stored in Core Data natively) and recreate the MKOverlay when needed. I think that's a much more efficient approach to be honest. I'm not sure how much of an impact creating an object has, so prove me wrong if I'm wrong.
You need to take the large dataset that composes the overlay and turn those individual data nodes into NSManagedObjects to be stored in CoreData.
I mean, you probably COULD just NSCoder the entire thing into one giant datablob, but at that point, you might as well just write the thing to a flat file (which frankly might be better if all you want to do is read/write it without changing it).
Don't use Core Data unless you're going to be doing legit querying or piecemeal changes to the dataset.

iOS: How to save a custom object that is created in an app

I've been reading up on saving objects, preserving state etc, but I'm still a little confused on the route I should take when saving objects that are created from my app. I have built an app where a user can create a Radio Station with the Genre, name and frequency. Once a user hits save, a "RadioStation" object is created using a custom class and is stored in an NSMutableArray that is placed in the RootViewController. While RadioStation objects can be stored in this array and are displayed correctly in the table view, it's obvious that once the application quits, these are no longer going to be held by the application.
At this point I'm not sure how I should begin to architect this design. Do I need to set up a plist? Should I use Core Data with a SQLite DB? Do I need to research iOS object serialization for both these scenarios? Or is there an more straight forward way that I'm not aware of?
There are three ways to persist data locally:
Store objects in a database using managed objects or SQLite
Store objects in a file in the sandbox using various classes' writeToFile
Store the data in NSUserDefaults
How you persist your object data depends on how you're using it, and what type of data you're saving. CoreData makes sense if you have a lot of objects of the same type and SQL's search and sorting mechanisms would be useful. I tend to use the sandbox for very large files, eg images, sound files, and large agglomerations of data bounced through NSData. And I use UserDefaults for small amounts of data that might be considered "preferences".
You can put anything anywhere you want, and I suggest being familiar with all three mechanisms when you start a new project. Additional techniques, like using NSCoding to serialize and deserialize classes, can be used to make saving data easier. Note that once you've implemented NSCoding, you could store data anywhere and even send it over the wire.
For your RadioStation, I would personally use UserDefaults. The benefit of using NSCoding is that, once you implement initWithCoder and encodeWithCoder, you can stream a hierarchy of objects (eg an array of custom objects, each of which might contain a dictionary, array, or custom object, etc) fairly easily. If the only thing you're saving is an array of strings, then directly using UserDefaults is easy enough.
The most powerful approach is to make your custom object a subclass of NSManagedObject, and store it in an NSManagedObjectContext using Core Data. On iOS this will always use an sqlite database, on Mac OS X you can use sqlite, xml, or a proprietary binary format (smaller/faster than xml).
Depending on how complicated your data structure is, and how likely it is to change in future, this technique may or may not be too complicated for your app. It's a lot of work to setup, and the API is very complicated, but it's the best choice if you need to store tens of thousands/millions of objects in a file.
For your app, I would have a single sqlite file containing all of the radio stations in the app.
The simplest option, is to implement the NSCoding protocol in your custom object's class. Documented here:
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html#//apple_ref/doc/uid/20000948-BCIHBJDE
The basic theory is your object writes or reads all it's data to a "coder", and then passes the coder on to any child object(s). Outside of the class, you use an "archiver" or an "unarchiver" class to coordinate everything.
Once your custom class implements NSCoding you can read/write it to the disk with:
myObject = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
[NSKeyedArchiver archiveRootObject:myObject toFile:filePath];
This is a very simple approach, but it has a few drawbacks, for example you will need to figure out your own way to support different versions of the file format (perhaps with a different file extension for example).
Many classes in Cocoa/CocoaTouch already implement NSCoding, so they can be written to a file in this fashion (including NSArray, NSString, NSNumber, etc).
For your app, i would use this to store each radio station in a single file. When the app launches, load all the radio stations from a directory. You should consider enabling iTunes drag/drop filesharing for your app, so users can easily share radio station files with their friends or between devices.
This would also fit in well with iCloud for syncing between devices, I haven't looked into iCloud much, but I expect one file per radio station will be the ideal way to implement iCloud syncing.
No need for such things. If you only have like one thing to save, then research this method of NSArray:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
I your user can save many radio stations, then you might want to look into CoreData, which has a bit of a curve, but ultimately will be the easiest way.