Are references maintained when archiving and unarchiving using NSKeyedArchiver? - ios

Suppose I do the following:
CarObject *car1 = [CarObject new];
CarObject *car2 = [CarObject new];
NSArray *carObjectsList1 = #[car1, car2];
NSArray *carObjectsList2 = #[car1, car2];
Also suppose that the car objects implement the NSCoding protocol. Let's say I archive and
unarchive:
//Archive
[NSKeyedArchiver archiveRootObject:carObjectsList1 toFile:#"list1.dat"];
[NSKeyedArchiver archiveRootObject:carObjectsList2 toFile:#"list2.dat"];
//Unarchive
NSArray *unarchivedList1 = [NSKeyedUnarchiver unarchiveObjectWithFile:#"list1.dat"];
NSArray *unarchivedList2 = [NSKeyedUnarchiver unarchiveObjectWithFile:#"list2.dat"];
Then suppose I do this:
CarObject *car1 = unarchivedList1 objectAtIndex:0];
car1.isTireFlat = true;
Would doing this change the property of "Car1" in both arrays? Meaning, would the first car object of both arrays have a flat tire? Are the reference links still preserved? Does anyone know why the answer is as such (as in, how are they preserved/why are they not preserved?)
Edit: I've edited my question to provide a more correct example.

If you archive two arrays that share the same object, you archive the that object twice. Then, when you unarchive those two arrays, you independently unarchive those arrays as if they were different objects. References aren't maintained upon unarchiving since each unarchive creates new object instances by calling initWithCoder: in the NSCoding protocol.

Yes. NSKeyedArchiver can even cope with reference cycles.

Related

Is it possible to save an NSMutableArray with NSKeyedArchiver into Core Data?

I have 2 NSMutableArrays in my project and was told that since Core Data doesn't support NSMutableArray that I would have to archive and unarchive it with NSKeyedArchiver to be able to save it to Core Data. I've implemented an NSFetchResultsController for my tableview, how would I go about saving the NSMutableArray in the the NSKeyedArchiver and then using that in Core Data? I'll post some code below of my arrays and the NSKeyArchiver.
- (instancetype)init {
self = [super init];
if (self) {
self.nameList = [NSMutableArray arrayWithObjects:#"1",
#"2",
#"3",
#"4",
#"5",
nil];
self.descArray = [NSMutableArray arrayWithObjects:#"desc1",
#"desc2",
#"desc3",
#"desc4",
#"desc5",
nil];
}
return self;
}
// How arrays are archived
NSData *titleData = [NSKeyedArchiver archivedDataWithRootObject: self.nameList];
.... //Not sure what to do with the archived data
NSData *descData = [NSKeyedArchiver archivedDataWithRootObject: self.descArray];
....
What would I do with the two arrays now that they are archived? How would I be able to add to them later?
My Core Data model is simple it is an entity called "Data" and has 2 attributes of type string called "title" and "desc". The 2 attributes are used to store the arrays.
My hope for this project is to allow the user to add new objects to the tableview, on top of the existing data that make up the cells already, and I want this done in Core Data. I'm aware of NSFetchResultController but am having trouble bringing this all together to get it to work, any help to steer me in the right direction is welcomed
You can save immutable data in core data. U can go with mutable to immutable after that you can by NSKeyedArchiver. Same things with NSUserDefaults.

Encoding excerpts from collection of objects that do not themselves conform to NSCoding

I would like to serialize excerpts from a collection of objects that do not themselves conform to NSCoding to a file. What's the best way to achieve this without transformation the collected objects an intermediary representation (one that would conform to NSCoding)? The (apparent) problem arises, because NSKeyedArchiver requires unique keys, and there is no NSArchiver on iOS.
For instance, the following will not work, because values are overwritten inside the loop as keys are reused. Calculating unique key strings from some loop index would be possible but quite a nuisance:
for (MyObject *object in myObjects) {
NSString *someString = myObject.someString; // excerpted string
[archiver encodeObject: someString withKey: #"someString"]
}
One way would be to encode the extracted excepts as an array:
NSMutableArray *extractedStrings = [NSMutableArray array];
for (MyObject *object in myObjects) {
[extractedStrings addObject:object.someString];
}
[archiver encodeObject:extractedStrings forKey:#"extractedStrings"];
Adding a category that conforms to NSCoding has proved to be the best solution in this case:
#interface MyClass (Coding) <NSCoding>
// ...

How does inheritance work in Objective-C?

What is the difference between
NSArray *arr1 = [[NSMutableArray alloc] init];
NSDictionary *dict1 = [[NSMutableDictionary alloc] init];
and
NSMutableArray *arr2 = [[NSMutableArray alloc] init];
NSMutableDictionary *dict2 = [[NSMutableDictionary alloc] init];
I know both (arr1, dict1, arr2, dict2) are going to create NSMutableArray and NSMutableDictionary object respectively, arr1 and dict1 would have access to all the functions that could be accessed by arr2 and dict2.
My Question to you guys is:
What is the difference between arr1 and arr2 or dict1 or dict2? Why would any one make object like arr1 and dict1 when we could do same things by making objects like arr2 and dict2? Are there any benefits of initializing objects like arr1 and dict1 over arr2 and dict2? If none, what could be the use of initializing objects like this?
arr1 is a reference to NSArray which gets an instance of a specialization of NSArray assigned, that is NSMutableArray. (NSMutableArray class inherits from NSArray and adds some methods to modify the array)
The compiler will prevent you from modifying arr1. (However, in Obj-C you could still call the NSMutableArray methods on arr1 during runtime when the object actually is of type NSMutableArray, but that is a different topic and not a proper way of doing it. See below for a better way of modifying arr1 if it really is mutable.)
The same applies for dic1.
arr2 and dict2 on the contrary are references to NSMutableSomething. Therefore those methods that belong to the mutable classes but not to their immutable base class can be accessed in code.
Some addition: Later in your code you could assign (and cast) arr1 to arr2 and then access the methods of NSMutableArray. Before assigning and casting a reference to a base class to a reference to a specialzation, you should always check the current class by isKindOfClass: method of NSObject.
Sampe:
if ([arr1 isKindOfClass:[NSMutableArray class]]) {
arr2 = (NSMutableArray*) arr1;
// Do something with arr2 that requires its mutability.
}
Mutable types have some special privileges like runtime update/insert/delete object from collection.
But they must have to be initialise before use and they occupies more memory than immutable.
Where else immutable type collection can not be alter at runtime. They are static containers as and contains elements which can't be alter from collection.
Immutable does not need to be alloc init and they consume less memory than mutable.
In short NSArray is not flexible. You can't add or remove object into it. Same goes for NSDictionary. You can initialize but dynamic cast will occur and you will have in arr1 simply NSArray.
Difference is simple
1-Mutable classes are those which could be changed after initialization but other can't be changed using SetObject: or SetValue: etc.
2-Mutable classes dynamically create on heap other are created on stack.
I prefer immutable on mutable because if mutable is exposed then other can
change its data using setobject or setvalue and may be we don't know about it.
but if immutable is exposed then we can't change its data so data remain protected.

Which is the best way to save data (numbers) in a plist file

Initially I had an object made of three properties (numbers 0 to 12). NSCoder and related issues made me avoid using an object and now I store three NSNumbers directly instead. I save a NSMutableArray with the three values in this way
NSMutableArray *data=[[NSMutableArray alloc] initWithObjects: cardSign, cardNumber, cardColor, nil];
I save and check if data are saved
NSLog(#"wrote %hhd", [data writeToFile:path atomically:YES]);
I try to retrieve the data:
NSArray *dataRead = [[NSArray alloc] initWithContentsOfFile:path];
if (dataRead)
{
cardSign = [dataRead objectAtIndex:0];
cardNumber = [dataRead objectAtIndex:1];
cardColor = [dataRead objectAtIndex:2];
}
Before saving the variable values are correct.
When I try to retrieve the values I get all 0 or (null).
Which is the best way to store three numbers in a plist file and how do I retrieve it?
Most likely your objects "cardSign", "cardNumber", and "cardColor", are not actual objects. When storing array contents or NSArray contents to a file all objects must be Apple recognized objects, NSString, NSNumber, etc...
If for instance cardNumber is defined as follows:
int cardNumber;
Then when "data" is initialized if should be something like (pay only attention to cardNumber)
NSMutableArray *data=[[NSMutableArray alloc] initWithObjects: cardSign, [NSNumber numberWithInt:cardNumber], cardColor, nil];
This line is taken directly from discussion section of the Apple API description of the "initWithContentsOfFile" call.
"The array representation in the file identified by aPath must contain only property list objects (NSString, NSData, NSArray, or NSDictionary objects). The objects contained by this array are immutable, even if the array is mutable.

iPad/iPhone 2D array serialization

I have a 2D-array that defines points in one or more paths:
Path#1 = (1,1) (3,3) (6,6)
Path#2 = (5,3) (15,5) (16,46)
Here is my code
NSArray path1 = make array of CGPoints
NSArray path2 = make array of CGPoints
NSMutableArray paths = [[NSMutableArray alloc] init];
[paths addObject:path1];
[paths addObject:path2];
Question: How do I serialize/deserialize this object?
As long as all of the objects in your arrays implement the NSCoding protocol, which most default classes do, you should be able to serialize the entire array structure using an NSKeyedArchiver.
If archiving/unarchiving directly to/from a file, it should look something like this:
NSString *filename = #"[filename]";
[NSKeyedArchiver archiveRootObject:paths toFile:filename];
paths = [NSKeyedUnarchiver unarchiveObjectWithFile:filename];
If you just want raw data, maybe to shove in NSUserDefaults or to transmit over the network, it should look something like this:
NSData *binaryData = [NSKeyedArchiver archivedDataWithRootObject:paths];
paths = [NSKeyedUnarchiver unarchiveObjectWithData:binaryData];
If you have custom classes in those arrays, you may have to implement NSCoder yourself, which is tedious, but not difficult.
Also, you may want to be careful with mutable arrays and dictionaries. I seem to remember something about mutable objects becoming immutable when they're archived, so you might want to check before you just start adding objects.
Further reading:
NSCoder Class Reference
Archives and Serializations Programming Guide

Resources