What is NSFrozenDictionaryM? - objective-c

I came across NSFrozenDictionary while debugging an app.
Shared index property declared as NSDictionary * sharedIndex = ...
What is it? How is it different from NSMutableDictionary?

It is an NSMutableDictionary marked as immutable.
One case to get __NSFrozenDictionaryM:
Have an array of mutable dictionaries:
NSArray *array = #[{NSMutableDictionary}, {NSMutableDictionary}, {NSMutableDictionary}]
Making a two-level-deep copy of it by:
NSArray *res = [[NSArray alloc] initWithArray:array copyItems:YES]
The resulting res array contains immutable copies of NSMutableDictionaries in array, which are of type __NSFrozenDictionaryM. I guess this is an optimisation to avoid really copying all dictionaries in the original array.

It is one of the concrete subclasses that is part of the NSDictionary class cluster.
There is a more academic description on Apple's documentation site.
Essentially: don't worry about it. If you declared it as a plain NSDictionary, then treat it as such: an immutable dictionary. Foundation may create something else under the hood for optimization purposes, but as far as your code is concerned, it's still an immutable dictionary.

Related

Objective-C = operator vs stringWithString/arrayWithArray

I'm new to obj-c development but partly have background in C development. It might be a noob question but I couldn't get an exact answer in other places. What is the difference between these snippets for arrays and strings and possibly other types of objects:
NSArray *original = [NSArray arrayWithObjects:someObjects,nil];
//Case 1
NSArray *copy1 = original;
//Case 2
NSArray *copy2 = [NSArray arrayWithArray:original];
and for strings
NSString *original = #"aString";
//Case 1
NSString *copy1 = original;
//Case 2
NSString *copy2 = [NSString stringWithString:original];
If I make changes to copy1 and copy2 later will they be reflected on original objects? And does the same rules apply to other object types?
The second code snippet does for NSString what the first code snippet does for NSArray. There is no difference in the behavior, because both NSString and NSArray objects in Cocoa are immutable.
When you call [NSString stringWithString:original], Cocoa is smart enough not to create a new object: the reasoning behind this decision is that since original cannot be changed, there's nothing you could do to tell apart a copy from the original. Same goes for [NSArray arrayWithArray:original], because you get the same instance back.
Note: If someObjects is mutable, one could tell apart an array from its deep copy by modifying the object, and seeing if it changes in the other place. However, arrayWithArray: method makes a "shallow" copy, so you wouldn't be able to detect a difference even if the objects inside your array are mutable.
Your question is really about what objects pointers are pointing to. When you say make changes to copy1 and copy2 later, I guess you mean to the pointer contents, not to the object referenced by that pointer. This is a rather functional way to think, but it important non-the-less.
In your example, the array / string part doesn't matter, because you aren't doing anything with the objects, you are just doing things with the pointers to those objects.
original points to one object. copy1 points to the same object. copy2 points to a different object (but which, in this case, is a copy of the first object).
copy1 is not a copy, but another pointer to the same memory as original. copy2 is actually a copy, pointing at a different piece of memory.
If you modify copy1 (assuming it was mutable, which you example code is not), you are modifying original too, as they point at the same piece of memory.
If you modify copy2, original should be unchanged (generally speaking). In your array example, the objects in the array original and in the array copy2 are, I believe the same. So you in this case, you have two arrays, but they have in them the same objects.
NSArrays and NSStrings are immutable so you can't change them.
You can't add or remove objects from NSArray, but if you change some object in array, it will change in its copy because NSArray holds a pointer to it.

Incompatible pointer type assigning "NSMutableArray" to "NSArray"?

This is my code
NSMutableArray * reversedNamesArray; //theres about a thousand variable already stored in here, this is just for documentation
reversedNamesArray = [reversedNamesArray sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
//Incompatible pointer type assigning "NSMutableArray" to "NSArray"
The message above is what i get, i know what it means, but i don't want to take an extra step of copying an NSMutableArray of a thousand variable to a NSArray is there any cast i can use to fix the warning, it doesn't affect my code, but i just want a fix for it. and can you explain why they are not compatible, they NSMutableArray and NSArray should use the same amount of bytes so i don't see why they are incompatible in the first place.
-sortedArrayUsingSelector: is implemented in NSArray and returns an NSArray even when called on an NSMutableArray.
You should use one of the sort methods implemented in NSMutableArray. This would work:
[reversedNamesArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
#GerdK's answer is the right one, but I thought I would explain why you cannot just cast an NSArray into NSMutableArray. If you could, major optimizations would be lost. Consider the following:
NSArray *first = #[...];
NSArray *second = [first copy];
This is extremely cheap. second just adds an extra retain onto first and we're done. On the other hand:
NSMutableArray *first = [NSMutableArray arrayWith…]
NSArray *second = [first copy];
This is more expensive. In order to create second, we actually have to create new array and copy the pointers over and add extra retains.
Now, imagine what you're requesting were legal:
// Not legal, but let's say it were
NSArray *first = #[...];
NSMutableArray *second = (NSMutableArray *)[first copy];
Now [NSArray copy] has to be defensive against this. It has to implement copy as a full (expensive) copy. That's a real performance loss.
You might say "But I'll just use copy when I want to really copy and retain when I want to retain." Sure, but that's not good enough. Say I want to store an immutable copy of something I'm passed:
- (void)setSomething:(NSArray *)array {
_something = [array copy];
}
This is a very efficient and correct setter. If I pass a real NSArray, then it's cheap (and this is probably the normal case). If I pass an NSMutableArray (it's a subclass, so I can do that), then I automatically make a real copy (so the object can't change behind my back). You get this kind of optimization for free by keeping mutable and immutable objects separate. Otherwise, I'd have to interrogate the object and ask if it were mutable and then make decisions based on that.

Immutable NSDictionary with deeply nested hierarchy: change a value for a key?

If I have an immutable NSDictionary with nested hierarchy, from a JSON string, what is the easiest way to change a value for a key that is deeply nested in the hierarchy?
For example, I have a dictionary, and the value for "key1" is an array, inside the array, each element is a dictionary, and inside each dictionary, there is a value for key "key2", now I want to change the value for "key2", since the whole data structure is immutable, which makes it difficult, should I duplicate this data structure with mutable collection so that I can change that value, this seems to have a lot of overhead, but this is the only way that came into my mind.
I don't know if this is an acceptable alternative for you, but you can create the dictionary from the JSON string with the NSJSONReadingMutableContainers option, which creates all arrays and dictionaries as mutable objects.
i'm not totally sure, but i think you'll have to pass everyone of your dictionary and array to a mutable one.
It depends if you want to still have a immutable structure after the change or if it doesn't matter if it's still mutable after it.
if you want to stay immutable after the change, then you'll have to use temp variable for mutable dict and array.
NSMutableDictionary *rootDict = [NSMutableDictionary dictionaryWithDictionary:rootImmutableDict];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:[rootImmutableDict objectForKey:#"key1"]];
NSMutableDictionary* valueDict = [NSMutableDictionart dictionaryWithDictionary:[mutableArray objectAtIndex:idx]];
[valueDict setObject:newValueObject forKey:#"key2"];
[rootImmutableDict release];
rootImmutableDict = nil;
rootImmutableDict = [[NSDictionary alloc] initWithDictionary:rootDict];
if it doesn't matter for you if it's mutable, then you'd have to make it mutable when retrieving it from the JSON by using a temp immutable structure and make it mutable permanently.
i hope it'll be helpfull to you.

When does -copy return a mutable object?

I read in Cocoa and Objective C: Up and Running that -copy will always return an immutable object and -mutableCopy will always return a mutable object:
It’s important to know that calling -copy on a mutable object returns an immutable
version. If you want to copy a mutable object and maintain mutability in the new version,
you must call -mutableCopy on the original. This is useful, though, because if you want
to “freeze” a mutable object, you can just call -copy on it.
So I have something like this:
NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
NSLog( #"%#", [req className] ); // NSMutableURLRequest
NSLog( #"%#", [[req copy] className] ); // NSMutableURLRequest
NSLog( #"%#", [[req mutableCopy] className] ); // NSMutableURLRequest
According to this previous answer:
You cannot depend on the result of copy to be mutable! Copying an NSMutableArray may
return an NSMutableArray, since that's the original class, but copying any arbitrary
NSArray instance would not.
This seems to be somewhat isolated to NSURLRequest, since NSArray acts as intended:
NSArray *arr = [[NSMutableArray alloc] init];
NSLog( #"%#", [arr className] ); // __NSArrayM
NSLog( #"%#", [[arr copy] className] ); // __NSAraryI
NSLog( #"%#", [[array mutableCopy] className] ); // __NSArrayM
So...
When does -copy return an immutable object (as expected) and when does it return a mutable object?
How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
I think you've uncovered a great rift between documentation and reality.
The NSCopying protocol documentation claims:
The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.
But this is clearly wrong in some cases, as you've shown in your examples (and I've sent feedback to them about this via that documentation page).
But(#2) in my opinion, it doesn't actually matter and you shouldn't care.
The point of -copy is that it will return an object you can use with the guarantee that it will behave independently of the original. This means if you have a mutable object, -copy it, and change the original object, the copy will not see the effect. (In some cases, I think this means that -copy can be optimized to do nothing, because if the object is immutable it can't be changed in the first place. I may be wrong about this. (I'm now wondering what the implications are for dictionary keys because of this, but that's a separate topic...))
As you've seen, in some cases the new object may actually be of a mutable class (even if the documentation tells us it won't). But as long as you don't rely on it being mutable (why would you?), it doesn't matter.
What should you do? Always treat the result of -copy as immutable, simple as that.
1) When does -copy return an immutable object (as expected) and when does it return a mutable object?
you should always treat it as the immutable variant. the mutable interface of the returned type should not be used. apart from optimizations, the answer should not matter and should be considered an implementation detail unless documented.
the obvious case: for a number of reasons, objc class clusters and class designs can be complex. returning a mutable copy could simply be for convenience.
2) How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
using the copy constructor of the immutable class is a good way (similar to St3fan's answer). like copy, it's not a guarantee.
the only reason i can think of as to why you would want to enforce this behaviour is for performance or to enforce a restricted interface (unless it's academic). if you want performance or a restricted interface, then you can simply encapsulate an instance of the type which copies on creation and exposes only the immutable interface. then you implement copy via retain (if that's your intent).
alternatively, you can write your own subclass and implement your own variant of copy.
final resort: many of the cocoa mutable/immutable classes are purely interface - you could write your own subclass if you need to ensure a particular behaviour -- but that's quite unusual.
perhaps a better description of why this should be enforced would be good - the existing implementations work just fine for the vast majority of developers/uses.
Bear in mind that there is not one copy implementation -- each class implements its own. And, as we all know, the implementation of the Objective C runtime is a little "loosey goosey" in spots. So I think we can say that mostly copy returns an immutable version, but some exceptions exist.
(BTW, what does this do:
NSArray *arr = [[NSMutable array] init];
?)
The best way to turn an object into an mutable one is to use the mutable 'constructor'. Like for example:
NSArray* array = ...;
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray: array];
Copy is used to make a copy of an object. Not to change it's mutability.

Creating an NSArray initialized with count N, all of the same object

I want to create an NSArray with objects of the same value (say NSNumber all initialized to 1) but the count is based on another variable. There doesn't seem to be a way to do this with any of the intializers for NSArray except for one that deals with C-style array.
Any idea if there is a short way to do this?
This is what I am looking for:
NSArray *array = [[NSArray alloc] initWithObject:[NSNumber numberWithInt:0]
count:anIntVariable];
NSNumber is just one example here, it could essentially be any NSObject.
The tightest code I've been able to write for this is:
id numbers[n];
for (int x = 0; x < n; ++x)
numbers[x] = [NSNumber numberWithInt:0];
id array = [NSArray arrayWithObjects:numbers count:n];
This works because you can create runtime length determined C-arrays with C99 which Xcode uses by default.
If they are all the same value, you could also use memset (though the cast to int is naughty):
id numbers[n];
memset(numbers, (int)[NSNumber numberWithInt:0], n);
id array = [NSArray arrayWithObjects:numbers count:n];
If you know how many objects you need, then this code should work, though I haven't tested it:
id array = [NSArray arrayWithObjects:(id[5]){[NSNumber numberWithInt:0]} count:5];
I can't see any reason why this structure in a non-mutable format would be useful, but I am certain that you have your reasons.
I don't think that you have any choice but to use a NSMutableArray, build it with a for loop, and if it's really important that the result not be mutable, construct a NSArray and use arrayWithArray:
I agree with #mmc, make sure you have a valid reason to have such a structure (instead of just using the same object N times), but I'll assume you do.
There is another way to construct an immutable array which would be slightly faster, but it requires creating a C array of objects and passing it to NSArray's +arrayWithObject:count: method (which returns an autoreleased array, mind you) as follows:
id anObject = [NSNumber numberWithInt:0];
id* buffer = (id*) malloc(sizeof(id) * anIntVariable);
for (int i = 0; i < anIntVariable; i++)
buffer[i] = anObject;
NSArray* array = [NSArray arrayWithObjects:buffer count:anIntVariable];
free(buffer);
You could accomplish the same thing with even trickier pointer math, but the gains are fairly trivial. Comment if you're interested anyway.
Probably the reason there is no such method on NSArray is that the semantics are not well defined. For your case, with an immutable NSNumber, then all the different semantics are equivalent, but imagine if the object you were adding was a mutable object, like NSMutableString for example.
There are three different semantics:
retain — You'd end up with ten pointers to the same mutable string, and changing any one would change all ten.
copy — You'd end up with ten pointers to the same immutable string, or possibly ten different pointers to immeduable strings with the same value, but either way you'd not be able to change any of them.
mutableCopy — You'd end up with ten different mutable string objects, any of which you could change independently.
So Apple could write three variants of the method, or have some sort of parameter to control the semantics, both of which are ugly, so instead they left it to you to write the code. If you want, you can add it as an NSArray category method, just be sure you understand the semantic options and make it clear.
The method:
-(id)initWithArray:(NSArray *)array copyItems:(BOOL)flag
has this same issue.
Quinn's solution using arrayWithObjects:count: is a reasonably good one, probably about the best you can get for the general case. Put it in an NSArray category and that's about as good as it is going to get.