I'm working on client/server application which uses AsyncSocket. For transferring data, it uses NSData.
How can I insert my custom object, containing NSNumbers, NSIntegers, and NSStrings into an NSData object and then get it back out?
One way to insert (serialize) a custom object into an NSData object is to use NSCoding and NSKeyedArchiver.
First, have your custom object implement the NSCoding protocol.
Example here:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Archiving/Articles/codingobjects.html#//apple_ref/doc/uid/20000948-97234
Then, for information on using your object with NSKeyedArchiver refer to:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/Archiving/Articles/creating.html
Hope that helps!
Related
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.
I am reading "Core Data Programming Guide". It contains this text:
You must, however, change attribute values in a KVC-compliant fashion.
For example, the following typically represents a programming error:
NSMutableString *mutableString = [NSMutableString stringWithString:#"Stig"];
[newEmployee setFirstName:mutableString];
[mutableString setString:#"Laura"];
For mutable values, you should either transfer ownership of the value
to Core Data, or implement custom accessor methods to always perform a
copy. The previous example may not represent an error if the class
representing the Employee entity declared the firstName property
(copy) (or implemented a custom setFirstName: method that copied the
new value). In this case, after the invocation of setString: (in the
third code line) the value of firstName would then still be “Stig” and
not “Laura”.
Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?
Question regarding copy and programming practice:
From what I have read here:
NSString property: copy or retain?
I understand
that using copy will ensure that firstName is "Stig", not Laura
it is wise to do so because "in almost all cases you want to prevent mutating an object's attributes behind its back"
I would really like to know what is the above quoted text trying to tell us in the context of Core Data. We have to use "copy" anyway whether using Core Data or not. Also, I would be glad if someone could throw more light on point "2" (it is wise to...) above as in what will be the consequences of mutating an object's attributes behind its back?
your "Question regarding text: "In this case" is which case--the one where property is declared as "copy" or when its not?"
mis-matched the point that Apple document wants to explain, I believe.
As Apple document points out, if custom-accessor-method is implemented normally, the default implementation does NOT copy attribute values. If the attribute value may be mutable and implements the NSCopying protocol (as is the case with NSString, for example), you can copy the value in a custom accessor to help preserve encapsulation (for example, in the case where an instance of NSMutableString is passed as a value).
Here is a copying setter snippet
#interface Department : NSManagedObject
{
}
#property(nonatomic, copy) NSString *name;
#end
#implementation Department
#dynamic name;
- (void)setName:(NSString *)newName
{
[self willChangeValueForKey:#"name"];
// NSString implements NSCopying, so copy the attribute value
NSString *newNameCopy = [newName copy];
[self setPrimitiveName:newNameCopy];
[self didChangeValueForKey:#"name"];
} #end
The issue is when to use (and how) immutable values.
Since core data use KVO heavily when detecting changes done to objects, if you use a mutable property that is changed directly through it object and not through the property, CoreData will not detect the change to the object and your changes might not persist to the store.
If you use mutable NSManagedObject attributes, override the setter/getter method and use only them to mutate the underlying object (this mean that you are responsible to let CoreData know that a change did happen to the object, and it must be persisted to the store.
Also, if you use transformable properties for complex objects, you must trigger the change notifications yourself in order for CoreData to realise that a change has occurred, and the object should be re-transformed and saved when the context saves.
I would highly recommend that when it comes to simple objects like strings, you use immutable property values which will force you to go through the object properties and trigger the default KVO notification (copy attributes will also force the KVO notifications).
I recently wanted to store and retrieve NSArrays from user defaults, and I found and used the suggestion at Storing custom objects in an NSMutableArray in NSUserDefaults, which works fine.
In retrospect, however, it would seem this is overkill if you are using standard collection classes, not custom objects. Can anyone confirm that the main benefit of the proposed solution I am using (linked above) is that you can store custom classes in this way, but that for working with NSArray where the contents are strings or other arrays or standard dictionaries, this approach with the NSKeyedArchiver is unnecessary?
Yes, see the NSUserDefaults documentation at:
setObject:forKey:
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
You only need to use an NSCoder like NSKeyedArchiver if you need to covert your object to NSData.
I have a core data entity called Hospitals. There are three fields: name(string), latitude(double), and longitude(double).
I have an NSManagedObject class called Hospitals.h/.m that lets me fetch data just fine.
I also have determined my current CLLocation.
Now, when I load all of my hospital records, I would like to step through them and calculate my current distance from the hospital. No problem.
Ah, but now I need to store the CLLocationDistance somewhere. At first, I just created a new class with iVars to hold 1) the HospitalManagedObject and 2) the CLLocationDistance. But that became very awkward.
So, I thought I would just extend the Hospitals managed object class to include a variable "myDistance." But everything I am trying causes the sub-classed managed object to stop wanting to communicate with core data.
How is the best way to do this?
Any insights are very-much appreciated.
You could add a new field to your object model for Hospital object called something like myDistance and make it of type Undefined in the datamodel. This will tell Core Data the you have a property that you are going to use at runtime but it isn't something that is going to be stored in the database.
In your Hospital.h you define the property using whatever type you want (I am assuming the distance is an NSNumber) like this
#property (nonatomic, retain) NSNumber *myDistance;
and in your implementation you can put it in a #dynamic myDistance; statement just like all of the other data fields.
I'm sure this must be a stupid question, but I've scoured the interwebbings and can't find the answer. Plenty of people talk about using the Generation Gap pattern, in which you have an NSManagedObject subclass that is generated from your model, and then subclass that to add transient properties and behaviours. The benefit of this is that if you change your persisted model, you can just generate your base files again without any danger of overwriting your own code.
I have a base CardMO object derived from NSManagedObject. I then subclass this to make my own Card object.
My question is, how do I create a Card object that is managed?
I tried:
Card* card = [NSEntityDescription insertNewObjectForEntityForName:#"CardMO" inManagedObjectContext:moc];
But this object isn't really a Card, and of course there's an exception when I go on to call a Card method on this object.
I've tried creating a Card with alloc / init (where init just calls [super init]), and then adding it to the managedObjectContext like this:
[moc insertObject:(CardMO*)card];
This gives me the cryptic error "Failed to call designated initializer on NSManagedObject class 'Card'"
I've tried modifying this by calling [NSEntityDescription insertNewObjectForEntityForName:#"CardMO" inManagedObjectContext:[AIStoreManager sharedAIStoreManager].managedObjectContext] instead of [super init]. In this case the object I get back is again a CardMO, and I can't call Card methods on it.
What should I be doing?
And (for bonus points :-) - after I've passed this hurdle, I need to create my other Card objects from XML, and then turn them into NSManagedObjects. I'm using code based on Apple's XMLReaderSAX - I hand this code a chunk of XML, and it gives me back an array of many Card objects. Can I later add these objects to my managed object context, or do I have to get into XMLReaderSAX and change how it creates those objects?
I'm writing an iPhone app on 3.0, but I assume this is the same for Core Data for 10.5.
Sussed it!
In the data model, the class name for the entity has to be that of the most derived class - Card in my case. The trouble then is that you have to remember to change the name to CardMO before generating any new files.
This must be why people use mogenerator.
Why not use NSCoding? Then you could support XML or JSON or any other means of describing the object outside your application.