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.
Related
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.
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.)
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 created one custom class & created object for that class. I want to store that object into NSUserDefault. I am getting error while set the object into userfaults. How can I set?
You have to implement the NSCoding protocol in your class & implement the protocols methods.
This is simply serialization. While storing the object you should serialize(NSKeyedArchiver) the class & same as retrieval you should unserialize(NSKeyedUnArchiver) that object.
the methods are
(void)encodeWithCoder:(NSCoder *)aCoder;
(id)initWithCoder:(NSCoder *)aDecoder;
As the documentation states:
A default object must be a property list, that is, an instance of (or
for collections a combination of instances of): NSData, NSString,
NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any
other type of object, you should typically archive it to create an
instance of NSData.
The simplest way of creating an instance of NSData is by using an NSKeyedArchiver.
The most probably reason is that you have something hard to archive in your structure.
Here is a quote from the NSUserDefaults Class Reference that you might find useful:
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. For more details, see Preferences and Settings Programming Guide.
Edit.
Beaten to the punch by Stephen, give the man a prize :)
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?