NSMutableDictionary doesn't get written to file - ios

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?

Related

Xcode Duplicate NSDictionary

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.

Lazy initialization in iOS

Data comes from the server in JSON, which is placed in a NSDictionary. Depending on type of requested data the new class object will be created from this NSDictionary. There're a lot of data comes, so the object holds a reference to NSDictionary and extracts a value only when referring to a particular variable. Something like lazy initialization:
- (NSString *)imgURL {
if (_imgURL == nil) {
_imgURL = [self makeObjForKey:kImageURL];
}
return _imgURL;
}
This significantly increases application speed, but produces other problems:
If a value is absent in NSDictionary, it remains nil. Then for each subsequent call to this variable there occurs search for it in NSDictionary.
When copying the entire instance of the class (NSCopying protocol), all variables are copied, producing convertion from entire NSDictionary.
Solutions:
Put some flag indicating that value has been checked. But then you
have to add additional checks
Only copy NSDictionary for object instance, but then later have to
parse same variables again
Anyway these solutions are not optimal. Maybe somebody faced with a similar problem and can advise other techniques.
NSDictionary lookups are very fast. Unless this is a truly enormous dictionary, I wouldn't worry too much about the lookup. If you have some properties that are checked particularly often, then you could optimize them with a special flag, but I usually wouldn't worry about it.
For copying, my first recommendation is to make this object a value (immutable) object. Initialize it once with JSON and then provide no mutators (that may be what you're doing already). Then copy is trivial; just return self. You don't need to make a real copy, because all copies are interchangeable. One of the great benefits of value objects is how cheap and easy they are to copy.
If you sometimes really need a mutable version, then follow the NSArray/NSMutableArray pattern. Then only the mutable version will have to deal with copies (in which case you should just copy the dictionary; not the cached objects).

How do I control what objects to encode using NSKeyedArchiver?

I have an array filled with two different types of custom classes. Lets call them ClassA and ClassB.
I want to enabled encoding/serialization for ClassA, but not for ClassB.
I am using [NSKeyedArchiver archiveRootObject:toFile] to serialize the list to disk.
I want NSKeyedArchiver to ignore all objects of type ClassB.
I dont want to iterate though the list and remove all ClassB objects, as I want to keep the original list in memory.
You can either subclass NSArray and override the -encodeWithCoder: method or copy and filter the original and then archive the copy rather than the original.

IOS: Do i need to retain key in dictionary ios?

I have dictionary in which i m storing
1)keys = which is string attribute of an "SimpleObject" with 'assign' property
--2) value = "SimpleObject"
NSMutableDictionary retains the object so im releasing the object. the key is present in the same object. The key is string.
Now do i need to explicitly retain the string key before add it to dict ?
No, you do not need to explicitly retain the NSString, because your object already does that. Since the dictionary retains the object, the NSString is safe from being released prematurely.
Moreover, NSDictionary makes a copy of your string key, as a precaution against using a mutable object as a dictionary key (which is very bad). That's why you are free to change that key inside the object as you please. Of course that would not change the placement of the object inside the dictionary.
The answer is no. Whether or not you are using ARC. No.
No, you do not need to retain the keys (or the values) of an NSDictionary. This is because NSDictionary copies the key. When you retrieve an objects with objectForKey: isEqual: is used to determine which key refers to the object you passed in.
The basic rule in manual memory management in Cocoa is -- worry about what you're doing in that object or method; don't worry about what any other object is doing.
All you're doing is passing the key to a method of the dictionary. You are not storing it around anywhere. Whatever the dictionary does with it, it is responsible for the proper memory management. What it does is none of your business.
(There is a slight exception with blocks, in that you sometimes must copy them before passing to a type-agnostic method. But let's not worry about this for now.)

NSUserDefaults and primitive data types?

What is the point of the NSUserDefaults methods such as -setFloat:forKey: and -floatForKey: when -registerDefaults: accepts only a NSDictionary which can't hold any primitive data types - only objects.
Instead it seems I have to use -setObject:forKey and -objectForKey: and store only NSNumber objects if I want to be able to give my floats any actual default values.
What am I missing here?
setFloat: is just a convenience method that creates an NSNumber and then passes that to setObject:. floatForKey: does the reverse.
NSDictionary can only hold object types, so you need to wrap primitives in the appropriate objects. So yes, you do need to do what you are doing to set up the default defaults.
It would be nice if you could use those methods directly on an NSDictionary, that would be a pretty trivial category to write.

Resources