NSMutableDictionary setObjetct forKey id NSString - objective-c

Frankly, it is rather a detail question.
Apples documentation of NSMutableDictionary https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSMutableDictionary_Class/Reference/Reference.html states:
setObject:forKey:
Adds a given key-value pair to the dictionary.
- (void)setObject:(id)anObject forKey:(id)aKey
According to that the parameter forKey accepts any object. However, when I try to pass an NSNumber Incompatible pointer types sending 'NSNumber *' to parameter of type 'NSString *'
Aparently some NSString only is accepted as key.
For the time beeing I will convert my number to a string. In the end it is just a key. But does anybody know who is right? The documentation or the compiler?

You shouldn't get that warning when using setObject:forKey:. However, you will get that warning when using the similarly-named setValue:forKey:. The latter, while it appears similar in name, is part of the key-value coding system and thus only accepts an NSString as the key.
Here's a sample program to demonstrate the difference.

Apple's documentation is right, maybe you were confusing the method setObject forKey with the setValue forKey as #mipadi said.

this works fine:
[someMutableDictionary setObject:#"a string" forKey:[NSNumber numberWithInt:1]];
what does your code look like?

Related

Objective-C - NSLog - "%#" Referencing Character

I'm working through a BNR iOS Programming text and I came across this piece of code:
NSLog(#"%#", [items objectAtIndex:i]);
I am unsure of what "%#" is used for. I've seen other formats for referencing integers or characters, but I've never seen this.
I even checked this reference here and it had nothing.
Thanks!
%# is for printing objective-c objects.
To be a bit more precise. Every object is able to override
-(NSString *)description
This method is called when you use %#. It depends on the object what info of the object it will return in the NSString.
According to Apple:
Objective-C object, printed as the string returned by descriptionWithLocale: if available, or description otherwise. Also works with CFTypeRef objects, returning the result of the CFCopyDescription function.
Reference:
String Format Specifiers
I am working through the same text - and coming from a Java background, the "%#" formatter seems equivalent to the "toString" method that Java would have.
It is used to display custom information about an object.
There is a good example in the response to this StackOverflow question about the "toString()" equivalent.

Why cast the NSNumber contents of an NSMutableArray when retrieving the intValue?

In some sample code, an NSNumber is added to a mutable array:
// In the .h, as an instance variable:
NSMutableArray *sequence;
// In the .m file:
sequence = [[NSMutableArray alloc] initWithCapacity:100];
[sequence addObject:[NSNumber numberWithInt:123]];
Then later on, when the integer is needed, it uses:
[(NSNumber *)[sequence objectAtIndex:aCounter] intValue]
I just wonder why the cast (NSNumber *) is needed? Because the program runs fine without it, too. Is it just a good practice? If so, what can it prevent from happening? If there is a bug so that one element is not an NSNumber *, then casting it can create strange behavior too.
Casting only makes the compiler believe that the object (which is returned as of type id, i. e. generic object type with no other information!) is actually an NSNumber, so that it can identify correclty its intValue etc. methods. It doesn't make things differ at runtime. If the object is not an NSNumber, then it will crash at runtime, with or without the casting.
It is fine to do it without the casting, the casting just makes it explicit that you treat it as an NSNumber, if you have a bug and this is not an NSNumber (or more precisly, don't respond to intValue) you'll get some odd behavior anyway.
//In objective-C any object can send message to any other object.
//SO here both statmenst are perfectly valid ,but
[(NSNumber *)[sequence objectAtIndex:aCounter] removeFromSuperview]; //This throws warning and lets u know removeFromSuperview shpuld'nt be called
[[sequence objectAtIndex:aCounter] removeFromSuperview];//here u wont get any warnig
I just wonder why the cast (NSNumber *) is needed?
It's not needed if the signature of the selector that's actually called at runtime is visible to the translation, and all selector signatures visible to the translation match for the selector that's called.
You're probably thinking "What? That's complicated! That's also error prone, especially as my programs evolve!"
If multiple selector signatures for the same selector are visible and you message id, then you should expect undefined behavior because objc collections aren't typed and the compiler may not match the correct selector (if your warning level is high and your includes are all correct, you can see a warning about this).
The simple way to avoid this is to reintroduce the correct type by assignment:
NSNumber * n = [array objectAtIndex:i];
int a = [n intValue];
or by casting:
int a = [(NSNumber*)[array objectAtIndex:i] intValue];
so the compiler can match the selector appropriately for the type, and also warn you when the object may not respond to a given selector, or if the parameters or return types do not match, or if the interface of the type you cast it to is not visible in the translation -- after all, you should have an idea of what the collection contains.
Introducing that type safety properly is a very good practice.
The cast is just needed to stop the compiler from complaining that it's not sure you know what you're doing.
One of the things that the compiler does for you as it compiles is to check whether the interfaces for the recievers of messages say that they respond to the messages you're sending (intValue in this case). The interface of NSNumber does indeed say that it responds to intValue, but the return type of objectAtIndex: is id, which is a generic pointer. The compiler has no way to know what the type of the object at the other end of that pointer is -- that won't be known until runtime.
The cast tells the compiler that you do indeed know the type and that it doesn't need to warn you (or, in some cases under ARC, give an error) about the fact that it's not sure if the receiver of the message responds.
Note that if you changed the class of the cast to something that didn't respond to intValue (such as NSDate), the compiler would gripe at you, but if the object really was still an NSNumber, the message would still succeed at runtime. Casting can't change the type of the object; it is simply an annotation for the compiler.*
*In some cases, it can also increase the readability of your code, too.

Incompatible Type for Argument 1 Objective-C

I have a function that sets an entity within a Core Data store. I used to have all values it would be storing as type double, however now I must make it accommodate NSStrings as well. Consequently, I changed the type of the parameter the function takes in, to an id type. However, now I get the error:
error: incompatible type for argument 1 of 'numberWithDouble:'
...at the following lines:
//...
[dfm setTimeStamp:[NSNumber numberWithDouble:value]];
//...
[[fetchedObjects objectAtIndex:0] setValue:[NSNumber numberWithDouble:value] forKey:#"timeStamp"];
//...
Apparently it doesn't like the [NSNumber numberWithDouble:value] segment of each line. I was contemplating making a container class that holds an NSNumber type (doesn't Apple already have a class like this?) to get around this problem, but I thought that there has to be an easier way I am not thinking of (besides duplicating the function and changing the type of the value parameter). Any ideas? Thanks in advance!
EDIT:
Here is the function declaration:
-(void)setItemInDFMWhilePreservingEntityUniquenessForItem:(attribute)attr withValue:(id)value
attribute is merely an enum which specifies which entity to store within. The problem is that the compiler is giving me problems with value being of type id, theoretically I can pass in anything I want, and I believe the way I have it I am implying that I will be passing it as an NSNumber, but the compiler doesn't like that as that is not actually a class instance I suppose?
The problem is that the compiler is
giving me problems with value being of
type id, theoretically I can pass in
anything I want, and I believe the way
I have it I am implying that I will be
passing it as an NSNumber, but the
compiler doesn't like that as that is
not actually a class instance I
suppose?
By declaring value as id, you can pass any object you want. But why do you "suppose" that NSNumber isn't an object, when it's clearly documented as being an object? The warning isn't about passing an NSNumber instance when you've declared value as an id - that's perfectly valid, because id means "any object," and an NSNumber instance is an object. The warning comes from calling +numberWithDouble:, a method that takes a double for its first argument, and passing it value, which is declared as id - i.e. an object. You can't pass an object to a method that expects a double.
Your proposed solution, typecasting value with (NSInteger)value will silence the warning, but it won't fix the problem. The typecast simply converts the memory address the object pointer targets to an integer value. If (as your edit suggests) value is already an NSNumber object, what do you hope to gain by creating another one, or by typecasting its memory address to an integer? Just do:
[dfm setTimeStamp:value];
The problem lies with the value variable. It should be declared as a double (primitive) for this call to succeed.
edit: after rereading your question, do a check in the function on the type of value, if it is an NSString (use [value isKindOfClass:[NSString class]]) store it as such, if its not then its a double (if thats the only two types you are passing) and store it as such.
Can't you just pass the NSNumber instead of double?
Just realized that the call I was making (numberWithDouble:) was having the compiler check for a primitive, i.e. double. Changing it to the following worked like a charm:
[dfm setTimeStamp:[NSNumber numberWithInteger:(NSInteger)value]];
Thanks to those that responded!

Advantage of data type id vs NSString in Objective C?

This code...
NSString * s = [[NSString alloc] initWithString:#"Hello, World"];
s = s.lowercaseString;
NSLog(#"%#", s);
...allows the use of dot notation but is strongly typed.
This code...
id s = [[NSString alloc] initWithString:#"Hello, World"];
s = [s lowercaseString];
NSLog(#"%#", s);
... is weakly typed and requires use of square brackets.
Other than that, is there any advantage of using one over the other?
If you're creating an NSString, then you might as well declare it as an NSString, and let the compiler help you.
The point of using id is to prevent strong coupling, and to use objects whose types are not known until a later time. e.g IBAction methods include the sender as a parameter as an id, because the exact type of the object isn't known.
Edited to add:
You may be new to the language, so I'll mention a couple of things
Firstly, where you have #"Hello, World", you already have an NSString, just one that is static. So you don't need to go through initWithString to create it. Just write:
NSString *s = #"Hello, World";
And, because you didn't alloc it, you don't have to worry about releasing it.
Secondly s.lowerCaseString. As Stephen has already answered, this is considered to be bad style. When you change a string to lower case, you aren't getting a property of the the string, you are causing an operation to be done on the string, in which case, you really should use bracket syntax.
Yes. The compiler warns you if you try to put a NSString into a method that expects a NSNumber.
It's more likely that the compiler finds your mistakes.
Arguably the former code is incorrect. You should only really use the dot notation to get/set properties, and lowercaseString is a method.
Otherwise, as you suggest, the only real difference is type safety. If you had a typo, say you put [a loercaseString], the compiler wouldn't shout at you.
There are certainly cases where you'd use id but your example is not one of them

Objective-C dot syntax tricks for when Key-Value won't work?

I'm iterating through some objects and trying to set some properties that, apparently, don't like Key-Value coding. so I'm unable to create a string at runtime to represent the "key".
This line of ViewController won't compile:
[self setValue:offsetX forKeyPath:[NSString stringWithFormat:#"myView%d.center.x", anInt]];
but I can set these properties with dot notation in a ridiculous switch statement:
myView1.center.x = offsetX;
Is there another way to go about this? perhaps create an accessor like myView(i).center.x ? Knowing full well it was going to be futile, i even tried: [NSString stringWithFormat:#"myView%d", anInt].center.x
to no avail...
The reason it wont compile is presumably because offsetX is an int or float, not an NSNumber (it would be helpful to give the compiler error message in your question).
However KVC and setValue:forKeyPath: is very clever and will automatically convert from an NSNumber for you, so use:
[self setValue:[NSNumber numberWithInt:offsetX] forKeyPath:[NSString stringWithFormat:#"myView%d.center.x", anInt]];
(or numberWithFloat as appropriate).