I have a requirement for a dictionary with keys that are not copied. This has lead me on a merry dance and I've ended up at the door of CFMutableDictionary.
I am trying to understand the extent to which they are interchangeable. In Apple's docs for CFMutableDictionary they state:
in a method where you see an NSMutableDictionary * parameter, you can
pass in a CFMutableDictionaryRef, and in a function where you see a
CFMutableDictionaryRef parameter, you can pass in an
NSMutableDictionary instance.
But I wondered whether it would be possible to cast a CFMutableDictionary to NSMutableDictionary and call NSMutableDictionary's methods on it, and it seems in some cases you can;
If I create a CFMutableDictionary using CFDictionaryCreateMutable() and cast it to an NSMutableDictionary.
I can call:
[cfDictionaryCastToNSDictionary objectForKey:someKey]
I can also call:
[cfDictionaryCastToNSDictionary setObject:someObject forKey:someKey]
... which will copy the key or raise an exception if it doesn't implement NSCopying. I can also iterate through its values using a for in loop.
However if I call:
[cfDictionaryCastToNSDictionary count]
I get an exception.
My question is what exactly is going on here under the hood? At no point does the Apple documentation mention being able to call some methods that do not exist on CFMutableDictionary by casting to NSDictionary.
If you are looking to understand problems using toll-free bridging, you should start with Mike Ash's "Toll-Free Bridging Internals" and go from there.
If you just want to get back to work, the answer is, don't do that; avoid the problem entirely by using the CF functions with the CFDictionary rather than toll-free bridging. The CFDictionary API should be enough to do whatever you're trying to do if the dictionary is being used purely internally.
Related
I was wondering what this convenience method stood for but was unable to find any answers for it.
What exactly does theDictionary[#"key"] do when wanting to extract a value from a dictionary? Is it using valueForKey: or objectForKey:?
What is better performance wise? Is it better to write the whole message like [theDictionary objectForKey:#"key"] or is the convenience method sufficient?
Let's have that in a context:
There are different ways to access an object that is accessable by a key.
A. Key-Value Coding
Key-value coding is done with -valueForKey: et al. Since this is implemented in NSObject nearly all objects having properties (have properties accessible by key) response to this. Basically this methods forms a selector from the key and sends it to the object. Beside this you have the ability to build key path and there are some special keys, i. e. count.
This is completely implemented outside the compiler.
Instances of NSDictionary supports this.
B. Subscripting
[] is the syntax for subscripting. If the key between [ and ] is an object, it is keyed subscripting. The compiler transforms that syntax into a -objectForKeyedSubscript: et al. message. The receiver has to implement the methods.
Instances of NSDictionary supports this by simply sending -objectForKey: et al. to self. But there is a little difference, see below.
C. NSDictionary
Additionally NSDictionary implements -objectForKey:.
There is no right or wrong. Personally I would prefer the most specific message for instances of NSDictionary, -objectForKey: et al. However, keyed subscription might be better readable.
Key-value coding is good, if you do not know the type of the receiver.
BTW: Key-value coding and keyed subscripting let's you set nil values in an instance of NSMutableDictionary, while -setObject:forKey: et al. forbids that.
NSDictionary provides the method objectForKey: to access its contents. The difference to valueForKey: is explained in depth in this related question: Difference between objectForKey and valueForKey?.
The [] operator is a shortcut to objectForKey:. Whichever you use doesn't really matter, but the former one is easier to read (less code noise), instantly clear in its intention, and you don't have to think about whether it's objectForKey: or valueForKey:.
So personally, I much prefer [].
I am going through some walkthroughs fpr Objective-C and I got to many places where I raised my eyebrows. I would love to get them down.
Is there a fundamental difference in message sending and method calling? Objective-C lets me do both: object.message yields the same result as [object message]. I think maybe nested messages cannot be created using the dot operator strategy?
I created an NSArray object, now I am about to print results for this using an NSEnumerator:
id myObject = [object objectEnumerator];
in a while loop iterating and printing results. The type of myObject is id, which means it's resolved at runtime and not compile time. I know very clearly what kind of objects are stored in my NSArray—they are NSStrings—so by changing the type of myObject to
NSString * myObject, it works just fine. However, I experimented and found out that myObject can be of any type, be it NSString or NSArray or NSEnumerator, and any of these work just fine, perfectly iterating the NSArray object and yielding the same results.
What's up with that?
I'm not sure what kind of distinction you're trying to make between "message sending" and "method calling", since they're two ways of describing the same thing. The dot syntax is just a shortcut for calling getters and setters, that is:
[foo length]
foo.length
are exactly the same, as are:
[foo setLength:5]
foo.length = 5
You should generally only use the dot syntax when you're using getters and setters; use the square bracket syntax for all of your other method calls.
For your second question: this is how dynamic typing works. Any type declarations you put in your code are hints to the compiler; your Objective-C method calls will always work as long as the objects respond to them.
It's a distinction oriented at the person reading your code. Dot syntax indicates state (I'm accessing an ivar), method syntax indicates behavior (I'm performing some action). To the runtime, both are the same.
I think Apple's intention is to show accessors as an implementation detail you shouldn't worry about. Even when they could trigger side effects (due to some additional code in the accessor), they usually don't, so the abstraction is imperfect but worth it (IMHO). Another downside of using dot notation is that you don't really know if there is a struct or a union behind it (which unlike message sending, never trigger side effects when being assigned). Maybe Apple should have used something different from a dot. *shrugs*
I think maybe nested messages cannot be created using the dot operator strategy?
Dot notation can be used to nest calls, but consider the following:
shu.phyl.we.spaj.da
[[[[[shu]phyl]we]spaj]da]
In this case, the uglier, the better. Both are a code smell because one object is creating dependencies to another object far far away, but if use brackets to pass messages you get that extra horrible syntax from the second line, which makes the code smell easier to notice. Again, convention is to use dots for properties and brackets for methods.
1: Your terminology is incorrect. The dot operator is not "method calling", a different operation. It's just a different visual appearance for message sending. There's no difference between [x y] and x.y. The dot syntax can only take one argument though, as it's intended to be used only for property access.
2: The static (compile-time) type of an object has no effect on its behavior at runtime. Your object is still an NSEnumerator even if you're calling it something else.
1) They're both message sending, just different syntax. [object message] is traditional syntax, object.message is the "dot notation", but means exactly the same thing. You can do some kinds of nesting with dot notation, but you can't do anything with methods that take complex arguments. In general, old hand Obj-C programmers don't use dot notation except for simple accessor calls. IMHO.
2) The runtime is really smart and can figure it out on the fly. The type casting of pointers is really just a clue to the compiler to let you know when you messed up. It doesn't mean a thing (in this case) when the message is sent to the array to fetch a value.
Message sending is the preferred way of doing this. It's what the community uses and reinforces the concept of objects sending messages to one another which comes into play later when you get into working with selectors and asking an object if it responds to a selector (message).
id is basically a pointer to anything. It takes some getting used to but it's the basis for how Objective-C handles dynamic typing of objects. When NSLog() comes across the %# format specifier, it sends a description message to the object that should should replace the token (This is implemented in the superclass NSObject and can be overridden in the subclass to get the desired output).
In the future when you're doing this, you might find it easier to do something like this instead:
for (NSString *s in YourNSArrayInstance) //assuming they are NSStrings as you described
{
NSLog(#"%#", s);
}
Or even simply just:
for (NSString *s in YourNSArrayInstance) //assuming they are NSStrings as you described
NSLog(#"%#", s);
You'll learn to like message sending eventually.
I would like to have a method along the lines of
setData:(SomeClassName *)data inPosition:(NSInteger)position
and in the implementation, check for nil as position. The idea is that if the position is provided, I will use it, and if not, I will allocate it automatically.
The problem is I can't pass either NULL or nil into this without a compiler warning.
I believe I have seen this pattern elsewhere (optional parameters). I think it might have been related to an NSIndexPath.
Should I use an NSNumber as a wrapper? or is there some other secret?
As an aside, I considered using separate methods - setData: and setData:inPosition:. But the problem is that 'data' is a core data created attribute, not a regular ivar, so when I actually want to set the value I would have to remember to send all the KVO messages. For example, inside setData:withPosition, I can't call the standard setData: - it would overwrite any work I did with the position.
Would also be interested in knowing which is the 'better' solution of these two.
#Justin's approach is generally the most appropriate. However, to your question about setData: and KVO, there are several things to note:
KVO notifications are sent automatically as long as the method is named setFoo:. Even if you override setFoo:, KVO will wrap your implementation with the correct KVO notification calls for the property. This is very likely the most magical thing in Cocoa. (I used to be certain it was the most magical thing, but I'm starting to wonder about block variable scoping, and especially how blocks are moved from the stack to the heap; that may be more magical.)
If you need to set a Core Data attribute directly, bypassing KVO and every other piece of possible magic, you can use the primitive accessor. setPrimitiveData: is the underlying method that setData: uses to set the property. You should not override the primitive accessors.
#Justin appears to have deleted his answer. The typical solution here would be to declare setData: and setData:inPosition: (btw, as a reader, I have no idea what "inPosition" means. I hope that it makes sense in context). setData: would call setData:inPosition: applying whatever is necessary to figure out "position."
Using the NSNumber wrapper is pretty standard.
Of course, you could always pass -1, NSNotFound, or define your own n/a value too.
There are three options:
Pass -1 or some such for "no value"
Use an NSNumber wrapper and pass nil for "no value"
Overload
You could try to use the Objective-C optional parameter mechanism, but that requires some sort of sentinel to mark the end of the list, so it's no better than any of the others.
I tried to figure out this code referencing: Cocoa: Dictionary with enum keys?
+ (NSValue*)valueWithReference:(id)target
{
return [NSValue valueWithBytes:&target objCType:#encode(id*)];
}
And,
[table setObject:anObject forKey:[NSValue valueWithReference:keyObject]];
But it feels something not good. Any recommendations?
You're absolutely right it's not good.
For one, you're encoding the wrong type (it should be #encode(id), not #encode(id*)), but in most cases this shouldn't cause a big problem.
The bigger problem is that this completely ignores memory management. The object won't be retained or copied. If some other code releases it, it could just disappear, and then your dictionary key will be a boxed pointer to garbage or even a completely different object. This is basically the world's most advanced dangling pointer.
You have two good options:
You could either add NSCopying to the class or create a copyable subclass.
This option will only work for objects that can meaningfully be copied. This is most classes, but not necessarily all (e.g. it might be bad to have multiple objects representing the same input stream)
Implementing copying can be a pain even for classes where it makes sense — not difficult, per se, but kind of annoying
You could instead create the dictionary with the CFDictionary API. Since Core Foundation types don't have a generic copy function, CFDictionary just retains its keys by default (though you can customize its behavior however you like). But CFDictionary is also toll-free bridged with NSDictionary, which means that you can just cast a CFDictionaryRef to an NSDictionary* (or NSMutableDictionary*) and then treat it like any other NSDictionary.
This means that the object you're using as a key must not change (at least not in a way that affects its hash value) while it's in the dictionary — ensuring this doesn't happen is why NSDictionary normally wants to copy its keys
For the later reference.
Now I know that there are some more options.
Override methods in NSCopying protocol, and return the self instead of copying itself. (you should retain it if you are not using ARC) Also you ensure the object to always return same value for -hash method.
Make a copyable simple container class holds strong reference to the original key object. The container is copyable but, it just passes original key when it being copied. Override equality/hash methods also to match semantics. Even just an instance of NSArray contains only the key object works well.
Method #1 looks pretty safe but actually I'm not sure that's safe. Because I don't know internal behavior of NSDictionary. So I usually use #2 way which is completely safe in Cocoa convention.
Update
Now we Have NSHashTable and NSMapTable also in iOS since version 6.0.
I'm not 100% sure about the correctness of this solution, but I'm posting it just in case.
If you do not want to use a CFDictionary, maybe you could use this simple category:
#implementation NSMutableDictionary(NonCopyableKeys)
- (void)setObject:(id)anObject forNonCopyableKey:(id)aKey {
[self setObject:anObject forKey:[NSValue valueWithPointer:aKey]];
}
- (id)objectForNonCopyableKey:(id)aKey {
return [self objectForKey:[NSValue valueWithPointer:aKey]];
}
- (void)removeObjectForNonCopyableKey:(id)aKey {
[self removeObjectForKey:[NSValue valueWithPointer:aKey]];
}
#end
This is a generalization of a similar method I saw online (can't find the original source) for using an NSMutableDictionary that can store objects with UITouch keys.
The same restriction as in Chuck's answer applies: the object you're using as a key must not change in a way that affects its hash value and must not be freed while it's in the dictionary .
Also make sure you don't mix -(void)setObject:(id)anObject forNonCopyableKey:(id)aKey and - (id)objectForKey:(id)aKey methods, as it won't work (the latter will return nil).
This seems to work fine, but there might be some unwanted side effects that I am not thinking of. If anybody finds out that this solution has any additional problems or caveats, please comment.
I have an object called Settings that inherits from NSMutableDictionary. When I try to initialize this object using
Settings *settings = [[Settings alloc] initWithContentsOfFile: #"someFile"]
it returns an object of type NSCFDictionary. As a result, it doesn't recognize my additional methods. For example, when I call the selector "save", it objects:
[NSCFDictionary save]: unrecognized selector sent to instance 0x524bc0
Of course, it's OK when I initialize using the garden variety
Settings *settings = [[Settings alloc] init]
I tried to cast it again to Settings but that didn't work. This seems really simple - what am I missing?
Thanks
NSDictionary is a class cluster. This means that the value returned from its init methods is not strictly an NSDictionary, but a subclass that implements the actual functionality. In almost every case, it is better to give your class an NSDictionary as an instance variable or to simply define a category on NSDictionary.
Chuck is correct about NSDictionary (and Dave, by extension, about NSArray/Set/String) and class clusters. Odds are that -[NSDictionary initWithContentsOfFile:] calls down to a different initializer than -init does, which is why it swaps out your allocated Settings instance for another subclass of NSMutableDictionary. (The initialization action when reading from a file may select a particular known subclass of NSDictionary which performs well for loading from a file, etc.)
I'll echo Chuck's guidance that it is almost always better to use composition or categories than inheritance for an NSDictionary. It's highly likely that you could accomplish what you're doing with categories in a much simpler way, and expose yourself to fewer potential bugs in the process. Consider yourself warned before deciding to subclass.
That being said, both NSDictionary and NSMutableDictionary have been designed to support subclassing, and on rare occasions that's the right thing to do. Think long and hard about it before trying it. If you find it's the right choice for your design, here are some key points to know and do as needed:
Override the following primitive methods from NSDictionary:
-count
-objectForKey:
-keyEnumerator
-initWithObjects:forKeys:count: (designated initializer)
Override the following primitive methods from NSMutableDictionary:
-setObject:forKey:
-removeObjectForKey:
If you're supporting NSCoding, be aware of classForKeyedArchiver and replacementObjectForKeyedArchiver: (both instance methods from NSObject) — they can totally change how your class responds, and you often unintentionally inherit some odd behavior from NS(Mutable)Dictionary. (You can verify if they are the culprit by setting a breakpoint on them, or implementing them to call super and breaking on your own code.)
I've implemented a number of these points in an NSMutableDictionary subclass of my own. You can check it out and use the code however may be helpful to you. One that particularly helped me (and could be the solution for your problem) was overloading the designated initializer, which is currently undocumented (Radar #7046209).
The thing to remember is that even though these bullets cover most common uses, there are always edge cases and less common functionality to account for. For example, -isEqual: and -hash for testing equality, etc.
If you actually read the spec for NSDictionary (a rash action, I know) you'll find a section named "Subclassing Notes". In it you will read:
If you do need to subclass NSDictionary, you need to take into account
that is represented by a Class cluster—there are therefore several
primitive methods upon which the methods are conceptually based:
initWithObjects:forKeys:
count
objectForKey:
keyEnumerator
In a subclass, you must override all these methods.
From https://stackoverflow.com/a/1191351/467588, this is what I did to make a subclass of NSDictionary works. I just declare an NSDictionary as an instance variable of my class and add some more required methods. I don't know what to call them though.
I posted my code sample here https://stackoverflow.com/a/10993594/467588.
This question is very old, and since most of these answers were posted, Apple has introduced object subscripting, which allows you to make your own classes behave more like NSMutableArray or NSMutableDictionary. This is simpler than the alternatives discussed above.
At a minimum, you have to override these methods:
//Array-style
- (id)objectAtIndexedSubscript:(NSUInteger)idx;
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)idx;
//Dictionary-style
- (id)objectForKeyedSubscript:(id <NSCopying>)key;
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)key;
Here's a nice tutorial on how to do just that.