Archiving an object and then unarchiving it - objective-c

NSString *cachePath= [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
cachePath= [cachePath stringByAppendingPathComponent:#"nerd.archive"];
// Load the cached channel
RSSChannel *cachedChannel= [NSKeyedUnarchiver unarchiveObjectWithFile:cachePath];
NSLog(#"unarchived data- %# %p, x value- %d",cachedChannel,cachedChannel,cachedChannel.x);
// if one hasn't already been cached, create a blank one to fill up
if (!cachedChannel) {
cachedChannel= [[RSSChannel alloc] init];
NSLog(#"cachedChannel initialised- %# %p",cachedChannel,cachedChannel);
cachedChannel.x=5;
}
In the above code-snippet, the pointer variable cachedChannel is assigned with the return value of unarchiveObjectWithFile: message. Now obviously in the first run, this would return nil but the pointer will be initialized later on in the “if-statement”. Lets say the cachedChannel var is something like
cachedChannel= [RSSChannel:0X123ff]
After the code has gone through its first run, the object assigned to cachedChannel would become serialized.
When i run the test project the second time and the unarchiveObjectWithFile: message is passed so that the serialized object is returned and assigned to the cachedChannel pointer var, it shows up as a different object with a different object-id.
Instead of cachedChannel pointing to [RSSChannel:0X123ff] object, it is now holding some other object like [RSSChannel:0X445ee]
How could this be possible?? Shouldn’t the object that was serialized before be the one to be unarchived later on with the same object-id residing in the same heap memory location?

How could this be possible?? Shouldn’t the object that was serialized before be the one to be unarchived later on with the same object-id residing in the same heap memory location?
Not at all. This is, as you say, happening later. And at this later time, the memory situation is completely different. Think of it this way: if you have code that creates an object from scratch, e.g. [[MyObject alloc] init], and you run the app today and then quit it and run the app again tomorrow, those two instances of MyObject, even though they play the very same role in the life of the app, will have two different memory addresses.
Moreover, what we are creating as we unarchive the object is a different instance from the one that was archived - identical, in whatever ways you have specified while archiving / unarchiving, to the original, but a different instance. Think of it this way: archive-unarchive is an elaborate way of making a copy of the object - and two copies of one instance are, obviously, two different objects.
After all, you could archive the object, hang on to the original, and immediately unarchive the archived object. That would be two different objects. But they could not possibly live at the same memory address!
It sounds like you may be trying to use the memory address as some sort of unique identifier. Beware of that. If a thing needs a unique identifier, give it a unique identifier as a property. Don't rely on the memory address at runtime for anything, except during debugging to confirm that two instances are one and the same instance.

Related

Different instances of NSDate pointing to the same allocated memory?

I'm creating tests where I have to make sure 2 different NSDate instances are really two different instances of allocated memory. So I have this example code:
NSDate *date1 = [NSDate date];
NSDate *date2 = [[NSDate alloc] initWithTimeInterval:0 sinceDate:date1];
XCTAssertEqualObjects(date1, date2);
XCTAssertNotEqual(date1, date2);
The first assert should compare object values using "isEqual", and it's working great!
The second assert should compare pointers using "==". The bizarre thing is that it sometimes fails randomly, telling me that both pointers have the same value (ie, they are pointing to the same allocated memory).
As I'm allocating twice, it is supposed to be different memory areas... So why do I have this test failing randomly sometimes? Maybe XCode is reusing memory areas somehow?
You can't reliably force the creation of separate objects. Some classes may use tagged pointers. The set of classes doing that can change over time with releases of the OS. A tagged pointer really just encodes the value of the object into a pointer-sized value. It doesn't allocate any memory. By definition, any two objects represented as tagged pointers whose values are equal will have equal "addresses".
Also, an init method is just a method. It can return any object it wants. There's no rule that it has to return the receiver. It can release the alloced object it is sent to (self) and return a different object. If it can determine that an existing object (such as the parameter you're passing to -initWithTimeInterval:sinceDate:) meets its needs, it may return that object (with an extra retain). This sort of thing is common in immutable value classes, like NSDate or NSString.
You're going to have to reconsider your supposed need to "make sure 2 different NSDate instances are really two different instances of allocated memory".

Returning NSArray element

In MRC code returning NSArray element like this:
NSString* MyName()
{
NSArray *names = [NSArray arrayWithArray:[[NSHost currentHost] names]];
return [names objectAtIndex:0];
}
void BullCrap()
{
NSString *wouldItBeRetainedAutomatically = MyName();
}
wouldItBeRetainedAutomatically in BullCrap() scope? I am assuming I don't need to send wouldItBeRetainedAutomatically retain message to make sure it is still available after names in MyName() is deallocated? Or names wouldn't be deallocated until BullCrap() is done?
NOTE: I know I should use ARC, but i can't.
In order to be completely sure the answer to this question is correct it would be necessary to see where listOfNames comes from. This will tell us what memory management contract has been made between the system framework and your calling code.
With that said, it is probably safe to say that the system framework has autoreleased the NSArray that was returned and stored in listOfNames. So listOfNames will get released and disappear as soon as the autorelease pool is flushed. You create the name array using arrayWithArray which will also return an autoreleased instance.
However, you are specifically asking about the memory management "state" of the first element of listOfNames (or names) that is returned by the MyName function. This has almost certainly only been retained by the listOfNames array (and the names array). (Again the caveat above still stands). Given that listOfNames (and names) will get autoreleased shortly, the elements of the array will also get released.
Therefore, you should probably return the element from MyName with a call to autorelease and then retain wouldItBeRetainedAutomatically in BullCrap if you need it to stick around. There is nothing in the code that you've shown that would suggest it will be retained automatically for you.
On a side note, the names array that you create is unnecessary in the example you give. The same could be achieved as follows:
NSString* MyName()
{
//listOfNames is returned by SystemFramework API
return [listOfNames objectAtIndex:0];
}
Using the intermediate names array simply confuses the matter.
In this case it looks like you shouldn't do anything, simply because you say listOfNames is returned by a system API and this method is (in a very contrived way) adding it to and then removing it from an array which will be destroyed. So, the array temporarily retains the listOfNames but doesn't really change anything and the listOfNames is (depending on how it was really created) already auto released.
Obviously there's a massive caveat on where listOfNames actually comes from...

Address of stack memory associated with local variable returned to caller?

The line
return (__bridge DCSDictionaryRef)d;
gives me a "Address of stack memory associated with local variable retured to caller?" warning. Ideally I think I ought to create a new copy of the DCSDictionaryRef instance but I'm absolute clueless about how it works.
Would appreciate any hints/help on how I can get rid of the warning (and a potential leak or the object being gcollected midway).
P.S. DCSCopyAvailableDictionaries and DCSDictionaryGetShortName are one of the 'undocumented' functions in CoreFoundation.
DCSDictionaryRef _DCSDictionary() {
NSArray *dicts = DCSCopyAvailableDictionaries();
for (NSObject *d in dicts) {
NSString *sn = DCSDictionaryGetShortName((__bridge DCSDictionaryRef)d);
NSLog(#"%#", sn);
if ([sn isEqualToString:#"Thesaurus"]) {
return (__bridge DCSDictionaryRef)d;
}
}
return NULL;
}
I believe you need one of the following:
A DCSDictionaryRef copy function
Documentation for the memory structure of DCSDictionaryRef
Very impressive hacking skills that allow you to archive a DCSDictionaryRef (and every object it points to) and unarchive it at another memory location
Here's Apple documentation about memory management for CoreFoundation. The gist is you need a Copy function applicable to the data type you are using. See if you can locate and call a DCSDictionaryRef copy function. The example in the linked documentation is that a function called CFStringCreateCopy would be used to copy a CFString object.
I've never tried something like this before, but it might be possible to archive the object (assuming you are able to know its size), but this also entails knowing which bits represent pointers, and subsequently archiving the objects the pointers point to as well, which also entails knowing the types and sizes of those objects. If you're able to successfully archive it, then you might have a shot at unarchiving it elsewhere in memory.
I think this is a very deep rabbit hole.

Use of an intermediary to initialize an object

I've always seen that we use an intermediary object, for example, creating an array to fill in another array:
characters = [[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil];
play.characters = characters;
[characters release];
with characters being an NSArray in the object play.
I saw the same thing with a #property and its self: we did not add the new items directly into this property, just as we don't directly fill in characters in the example above. Is this only about "style"?
This is not a matter of style.
play.characters is a property, and that can "contain" an existing array or nil, but even if it "contains" an existing array, you can't change the contents of an NSArray, so you'll have to create a new one and assign that to the property.
Assigning to a property will, if all was declared well, cause its setter method to run (which could be created by the compiler, if you used #synthesize, or written by you, in code) and that will take care of removing any existing array, assigning the new one and retaining it.
There is actually only one array in play in that little piece of code.
It is not the array that is intermediate, but the variable holding a pointer to it - in this case the variable characters.
This is what happens:
The expression
[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil]
allocates an object and initialises it with three NSStrings (which are themselves objects, but let's leave that out for a moment). The initialisation also includes an increment of the retain count, so it is one from the get-go.
This newly created object lives at a given position in memory, say 0100H. This position is then stored in the variable characters. In C terms we say that characters is a pointer to the object.
Then the property #"characters" of the object play is set to point to the same position in memory as the local variable characters. There are therefore now two variables (of which one is also a property) that point to the same object, or, if you prefer, to the same position in memory. If the property is of type retain, this will automatically increment the retain count of the object, so it is now 2.
With the release message in the last line, the object decrements its retain count by one, so at the end of this code snippet, the object is pointed to by the play.characters property, and it has a retain count of one.
To be really clean, this code should probably set the local variable to nil, to avoid confusion between variables holding pointers to the object and the retain count.
All this was meant to show that there really is only one array in play here, but two variables that point to it. So there are not as many computer resources being wasted as it might seem at a first glance.
If you wanted to do it all in a single line, you could write something like this:
play.characters = [[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil] autorelease];
but the exact working of this is less clear as it involves one of those mysterious autoreleases, i.e., a release that is handled automatically and postponed to some later stage.
This is a long description, but I hope it sheds some light on what is going on.

NSString retain copy question

I've seen a few posts on here about the issue of using retain or copy for strings. I still can't quite get my head around the difference or the importance.
In my case at the moment I have a class with a whole load of nsstrings to hold strings.
I want this class to only be instantiated once and I want its nsstring variables to change depending on the index clicked in a table view.
Would I be correct in saying that if I chose to use retain that my nsstrings would be overwritten each time I set their value on my tableview click and that if I chose copy I would somehow have 2 instances of each string....?
I'm sorry ..... I totally don't get it
This is a question about copying mutable objects vs. immutable ones. Since NSString objects are immutable (you cannot change their contents), they implement -copy like this:
- (id) copyWithZone: (NSZone *) zone
{
return [self retain];
}
If you think about it, there's no reason to duplicate an immutable object because that's a waste of memory. On the other hand, NSMutableString objects can see their contents change during their lifetime, so if you request a copy of an NSMutableString, you will get a real copy, a different object.
If your strings are not NSMutableStrings, it does not matter whether you retain or copy them. However, choosing the right method is important if you later refactor your code to use NSMutableStrings. A common logic should answer the following question for you: if I get an object whose contents may change outside, which value do I need? More often than not you will want to make a copy.