I am having an issue with two dictionaries. In my header file I declare a dictionary:
#property (strong, nonatomic) NSMutableDictionary *templateDictionary
The templateDictionary holds strings and dictionaries, such as 'blankCopy', which in turn holds other strings and dictionaries. I am also declaring a dictionary within my implementation file:
#property (strong, nonatomic) NSMutableDictionary *exampleDictionary
In my implementation I am trying to set part of the exampleDictionary to a portion of the templateDictionary like so:
[self.exampleDictionary setObject:[templateDictionary objectForKey:#"blankCopy"] forKey:#"template"];
This works until I change anything within exampleDictionary's 'template'. Now when I go back to the templateDictionary it has also changed. I figured this was because it was pointing back. I have tried the following but all don't have any effect:
[self.exampleDictionary setObject:[[templateDictionary objectForKey:#"blankCopy"] copy] forKey:#"template"];
[self.exampleDictionary setObject:[[templateDictionary objectForKey:#"blankCopy"] mutableCopy] forKey:#"template"];
[self.exampleDictionary setObject:[NSMutableDictionary dictionaryWithDictionary:[templateDictionary objectForKey:#"blankCopy"]] forKey:#"template"];
Any ideas as to what's wrong and how to fix it, or how to copy a dictionary object by values?
The problem here is that a dictionary only keeps references to the contained objects. When you copy a dictionary, you are only copying the reference, not the objects. So, if you modify something inside one dictionary, you'll see it modified in the other.
The solution would be to deep-copy the contents of the dictionary, that is, for each object stored in the dictionary, if it is also a dictionary, deep-copy it and store in the new dictionary.
You can look for example at this answer, which has the implementation.
First, an aside: you don't need to do this:
[self.exampleDictionary setObject:[templateDictionary objectForKey:#"blankCopy"] forKey:#"template"];
This is much easier to read:
self.exampleDictionary[#"template"] = templateDictionary[#"blankCopy"];
You will see old code written like the first line, but for new code the second line is far preferable. That syntax has worked in Xcode for at least two years now.
The real problem you have is that [NSMutableDictionary mutableCopy] is only a shallow copy -- it's a new dictionary, but the values in the dictionary are referenced, not copied. You need a deep copy. Check out http://samwize.com/2014/05/12/deep-copying-of-nsarray-slash-nsdictionary-slash-any-object/ for an approach using NSKeyedUnarchiver or https://gist.github.com/yfujiki/1664847 for a direct (but more verbose) approach.
Related
I have a CustomObject(of type NSObject) having an NSMutableArray property. This property contains NSDictionary objects. I need a deep copy of just the property to allow for editing in a different view controller; after editing, I desire to replace the original property with the edited deep copied property.
My question is:
Do I need to make a deep 'copyWithZone' ?
and if so, how do I implement it considering that it's just the single property that requires deep copying?
I've for now circumvented this by doing:
NSMutableArray *deepCopyArray = [NSMutableArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:<CustomObject>instance.<propertyToDeepCopy>]]];
Although this works as NSMutableArray & NSDictionary implement NSCoding, it doesn't feel the right way, or is it? Kindly help :)
It's an acceptable way with multiple merits:
It works
It's simple and obvious
It's very little code
It's self contained and thus easy to change in future
Any alternative would mean you iterating over the contents and manually copying everything yourself, there are a couple of reasons you might do that:
You know what is truly immutable and you can avoid copying
You need mutable containers (yhe dictionaries in the array
Though even in some of these cases you'd want to run a similar archive based process just using a property list instead (so you continue to write minimal code and leverage supplied SDKs).
So I'm using NSUserDefaults and I want to make sure I save an NSArray instead of an NSMutableArray. I'm dealing with an NSMutableArray prior to saving it, however, and I want to know how I'd best save it.
Should I do setObject:[mutableArray copy]? Or [setObject:(NSArray *)mutableArray]? A third option?
I was leaning toward copy, as it seems like the natural counterpart to mutableCopy, however wasn't copy used extensively pre-ARC for something different? I thought copy calls when autorelease was still prevalent were used for something distinct.
NSUserDefaults already saves a NSArray, even if you pass a NSMutableArray.
For other situations, copy with ARC is perfectly fine to use. Also, you don't need a cast because a NSMutableArray pointer, for polymorphism is compatible with a NSArray pointer (but not viceversa).
I'm new to objective c and I've got an interesting situation where I need two different NSDictionary objects that point to the same values. In that situation should I use strong or weak in the property declaration? Or should I do strong in one and weak in the other?
In Game.m
#property (strong/weak, nonatomic) NSDictionary* answers1;
In User.m
#property(strong/weak, nonatomic) NSDictionary* answers2;
In both cases the key will be an integer, but the value will be an answer object of my own making. Both answers1 and answers2 will need to exists for roughly the same amount of time. When it comes time to get rid of one it'll be okay to get rid of the other.
Both should probably be strong. Each class should do its own memory management without having to worry about what other classes are doing. Therefore, each should keep its own strong reference.
In this case, the best would actually be copy. This way, in addition to retaining the dictionary, you will create an immutable copy of the one passed to you, ensuring the dictionary does not get modified from an outside influence (for example, passing a mutable dictionary which can later mutate).
I'm trying to figure out how to create an NSMutableDictionary that retains instead of copies its keys. I have implemented -(NSUInteger)hash and -(id)isEqual: for my desired keys, I am just having trouble figuring out which options to specify in the callbacks.
CFDictionaryKeyCallBacks keyCallbacks = { 0, NULL, NULL, CFCopyDescription, CFEqual, NULL };
self.commonParents = (NSMutableDictionary*)CFBridgingRelease(CFDictionaryCreateMutable(nil, 0, &keyCallbacks, &kCFTypeDictionaryValueCallBacks));
The above code works correctly in ARC for using weak references to keys, but what if I want strong references? What should the key callbacks look like?
tl;dr:
Create a CFDictionaryRef with the provided default callback functions. It'll do what you want. Just don't call it an NSDictionary.
Yes, you can create a CFDictionaryRef that retains its keys and does not copy them. This is, in fact, the default behavior of a CFDictionaryRef.
The documentation for CFDictionaryCreateMutable() says:
If the dictionary will contain only CFType objects, then pass a pointer to kCFTypeDictionaryKeyCallBacks as this parameter to use the default callback functions.
(So if you're only going to be putting normal Objective-C objects into the array and not random things like void * pointers or whatever, this is what you want)
And the documentation for kCFTypeDictionaryKeyCallBacks says:
Predefined CFDictionaryKeyCallBacks structure containing a set of callbacks appropriate for use when the keys of a CFDictionary are all CFType-derived objects.
The retain callback is CFRetain, the release callback is CFRelease, the copy callback is CFCopyDescription, the equal callback is CFEqual. Therefore, if you use a pointer to this constant when creating the dictionary, keys are automatically retained when added to the collection, and released when removed from the collection.
Note that the retain callback is CFRetain() and not something like CFCopyObject (which doesn't actually exist).
In fact, Core Foundation doesn't have a canonical way to "copy any object", which is why functions like CFStringCreateCopy, CFArrayCreateCopy, CGPathCreateCopy, etc exist.
So, what you can do is create your dictionary like this:
CFDictionaryRef dict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
And you now have a dictionary that retains its keys and does not copy them.
I'm going to put the following bit in big letters so that you grok what I'm about to say:
This dictionary you've created is not an NSDictionary.
Yes, NSDictionary and CFDictionaryRef are toll-free bridged. But casting this CFDictionaryRef to an NSDictionary would be an abuse of that bridging, because of this line in the NSDictionary documentation:
...a key can be any object (provided that it conforms to the NSCopying protocol—see below)
Similarly, the documentation for -[NSMutableDictionary setObject:forKey:] explicitly says:
The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol).
The keys in your dictionary don't have to conform to <NSCopying> and are not copied using -copyWithZone:, which means your dictionary is NOT an NSDictionary (or NSMutableDictionary). Any time you see NSDictionary used in code, you should be providing a key-value map where the keys are copied (not retained). That is the API contract. To do anything else could result in undefined behavior.
(The fact that some objects override -copy to return [self retain] is an implementation detail and is not relevant to this discussion on "what is an NSDictionary".)
I think the best answer is buried in comment, so I'll highlight it here: The simplest approach is to use a +[NSMapTable strongToStrongObjectsMapTable] (or maybe one of the variants with weak references).
My suggest is that instead of doing this you subclass NSString or whatever class you're using as key, and override the copy method in a way that it returns the string retained, instead of a copied string.
I think there is 2 possibles solutions that could be achieved using plain old NSMutableDictionary. They are not as elegant as NSMapTable would be.
You state that each of your Key have a uniqueID, so I assume that this Value won't change over time.
Option 1 :
Use the uniqueID of your actual key to be the key of an NSMutableDictionary that would store NSArray of #[key, value] so the whole structure look like this
#{ key1.uniqueID : #[key1, value1], key2.uniqueID : #[key2 : value2] }
Option 2 :
Make a subclass of NSObject that is a wrapper around option 1. Or any variation on option 1.
Those are only valid if uniqueID never change
I have NSMutableDictionary object say obj. When I write it to the disk using
[obj writeToFile:filename atomically:YES] , the file does not get written to the disk. But the same set of statements work for a smaller mutable dictionary.
The first obj is (nonatomic, retain) property object of a class. The second smaller obj is a temporary local variable.
But the same set of statements work
for a smaller mutable dictionary.
That sets off a warning bell. Namely, when you are attempting to write the large dictionary to disk, what is in it? If you are using any of NSDictionary's file writing methods, they will only work with dictionaries that only contain instances of the classes blessed for use in property lists.
That is, if you have random other classes in there, the dictionary will not be written. Doesn't matter of the classes support NSCoding or not.
If you need to persist a dictionary with non-property list classes, you'll either need to use NSCoding or, more likely better, use Core Data.
This should work. Have you checked that obj isn't nil by some accident?