IOS: Do i need to retain key in dictionary ios? - 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.)

Related

Creating a Mutable Dictionary in Swift

I want to create a mutable dictionary which I can pass it to another controller so that both the dictionaries in different controllers points to the same memory location. If I change the value at another controller, it is also reflected in the previous controller.
This trick used to work fine with NSMutableDictionary without using any delegates.
My dictionary is of type: [String:AnyObject]
Swift collections are value types, not reference types and although you can pass value types by reference, that lasts only for the lifetime of the call.
What you're doing would be considered bad design — objects are sovereign, with well-defined interfaces, and encapsulated state; they do not informally pool state.
What you probably need to do is take your shared state, formalise an interface to it, and move it to your model. Each controller can separately talk to your model.
Swift's dictionary types are value types whereas your old NSMutableDictionary instances are reference types.
There is nothing that says you HAVE to use Swift's value types in the place of your old dictionary. If you have a good reason for using reference semantics with the dictionary, go ahead and leave it as an NSMutableDictionary and use the methods of that class to manipulate it. Just note in your code that you are using NSMutableDictionary explicitly because you want the reference semantics.

Assigning autoreleased object to a strong object

Lets say I have a class A with a member variable "myDictionary".
Now if I do this:
myDictionary = [NSDictionary dictionary];
I know that by default members of a class are of strong type. So myDictionary will be available during the lifetime of class A object. Thats what my understanding is about ARC.
But I am getting an EXE_BAC_ACCESS on myDictionary which really confuses me. Do I need to do anything extra in order to avoid EXE_BAD_EXCESS on myDictionary? Because above method returns an autoreleased object.
Thanks in Advance.
If you're not using ARC, then you should use +dictionary any time you
need an autoreleased dictionary, and +alloc/init any time you need a
dictionary that you're going to hold on to (e.g. by placing it in an
ivar), or alternatively if you simply want to avoid autorelease and
-release it manually.
So try to implement like this:
myDictionary = [[NSDictionary alloc] init];
The most likely error here is accessing myDictionary from multiple threads. Ensuring that you always access ivar correctly is one of the several reasons you should always refer to your properties as self.myDictionary rather than directly referring to the _myDictionary ivar. When you need to make this thread-safe, that will ensure there is only one place you need to fix the code.
There are other possible ways to get this error, such as incorrect handling of CFBridging... functions, but the most common is multi-threaded access.

NSMutableDictionary that retains its keys

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

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.

Where's the difference between setObject:forKey: and setValue:forKey: in NSMutableDictionary?

When looking at the documentation, I hardly see any big difference. Both "value" and "object" are of type id, so can be any object. Key is once a string, and in the other case an id. One of them seems to retain the object, and the other don't. What else? Which one is for what case?
setValue:forKey: is part of the NSKeyValueCoding protocol, which among other things, lets you access object properties from the likes of Interface Builder. setValue:forKey: is implemented in classes other than NSDictionary.
setObject:forKey: is NSMutableDictionary's reason to exist. Its signature happens to be quite similar to setValue:forKey:, but is more generic (e.g. any key type). It's somewhat of a coincidence that the signatures are so similar.
What adds to the confusion is that NSMutableDictionary's implementation of setValue:forKey: is equivalent to setObject:forKey: in most cases. In other classes, setValue:forKey: changes member variables. In NSMutableDictionary, it changes dictionary entries, unless you prefix the key with a '#' character -- in which case it modifies member variables.
So, in a nutshell, use setObject:forKey: when you need to work with dictionary keys and values, and setValue:forKey: in the rarer cases where you need to tackle KVP.
EDIT: and oh, it looks like this has been asked and answered before: Difference between objectForKey and valueForKey?
Another difference is that if you give a nil value to setValue:forKey:, it removes the key from the dictionary if it exists, otherwise does nothing. But if you give a nil value to setObject:forKey:, it raises an exception.
-setValue:forKey: just send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObjectForKey.
Dead simple.
anObject — The value for key. The object receives a retain message before being added to the NSDictionary. This value must not be nil.
aKey — The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). The key must not be nil.
value — The value for key.
key — The key for value. Note that when using key-value coding, the key must be a string (see “Key-Value Coding Fundamentals”).

Resources