Explanation for assigning Objective-C NSNumber object to int variable? - objective-c

Can anyone explain why this works in Objective-C? I would expect it to give an error since an object is being assigned to an int variable. I get that it does work, and this is great, but I am missing why this is allowed?
int i = [NSNumber numberWithInt:123];
Furthermore, this seems to store the wrong value (ie, on my system when I print out the value of "i" using NSLog I get "252711" instead of 123); however, only when the variable is declared and assigned in my main code. If I declare it as an instance variable of an object and then use Key-Value coding to assign it a value, it works properly:
Object instance variables...
#interface myObject : NSObject
{
int anInt;
}
Main code...
[myObject setValue:[NSNumber numberWithInt:123] forKey:#"anInt"];
NSLog(#"%#", [myObject valueForKey:#"anInt"]);
This code prints out "123" as expected, but I'm not sure why given that when using a seemingly similar approach above it does not work properly.

it doesnt "work" and there is a compiler warning about it. the reason it can be compiled is that the NSNumber class method numberWithInt returns a pointer, which can be implicitly converted to int. When you print it out you are getting the address where the objective-c object was allocated.
the SetValue:forKey: method doesnt take an int parameter, it takes an id which is just a pointer to a generic Objective-C object. Key-Value coding is taking care of assigning the intValue of the NSNumber object for you.

When you executing the code:
int i = [NSNumber numberWithInt:123];
You just assigning pointer value to int.
numberWithInt: returns NSNumber* and it's a pointer, pointing to some place in memory where NSNumber object allocated.
And now value of i is not a 123 but decimal representation of NSNumber* pointer.
Read more about pointers in C/Objective-C.

Related

performSelector causes leak because not right type in objective c

I have the following code:
SEL moveAlongBoth = #selector(moveAlongX:andY:);
if ([p1 respondsToSelector:moveAlongBoth]) {
[p1 performSelector: moveAlongBoth
withObject: [NSNumber numberWithInt:1]
withObject: [NSNumber numberWithInt:1]];
}
I am getting a "performSelector may cause leak" warning.
But
[p1 moveAlongX:1 andY:1];
Works just fine.
I understand that I am getting the error because the values are set to (int) in the implementation and I am using NSNumber. Without changing the implementation, how would I go about declaring a number value to int (if possible)?
Why can't you just do this:
if ([p1 respondsToSelector:#selector(moveAlongX:andY:)]) {
[(id)p1 moveAlongX:1 andY:1];
}
By the way, the Cocoa naming convention would have you call this method moveAlongX:y:.
With regard to the second part. If you are in charge of the classes that may be the type of p1, then you could define a protocol with moveAlongBoth:: and instead of checking with performSelector check wit conformsToProtocol. Let's say the protocol's name is CanMoveAlong then you can cast it to
id <CanMoveAlong> canDo = (id<CanMoveAlong>)p1;
after you checked conformity and directly invoke the method
[canDo moveAlongX:1 andY:1];
Doing so you achieve both, you get rid of the warning and you can pass int directly without using NSNumber.

NSFastEnumeration object casting in ARC

I'm trying to implement the countByEnumeratingWithState:objects:count: method from the NSFastEnumeration protocol on a custom class.
So far I have it iterating through my objects correctly, but the objects that are returned aren't Objective-C objects but rather the core foundation equivalents.
Here's the part of the code that sets the state->itemsPtr:
MyCustomCollection.m
- (NSUInteger) countByEnumeratingWithState: (NSFastEnumerationState *)state
objects: (id __unsafe_unretained *)buffer
count: (NSUInteger)bufferSize {
// ... skip details ...
NSLog(#"Object inside method: %#", someObject);
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)someObject;
// ... skip details ...
}
Then I call the 'for..in' loop somewhere else on like this
SomeOtherClass.m
MyCustomCollection *myCustomCollection = [MyCustomCollection new];
[myCustomCollection addObject:#"foo"];
for (id object in myCustomCollection) {
NSLog(#"Object in loop: %#", object);
}
The console output is:
Object inside method: foo
Object in loop: __NSCFConstantString
As you can see, inside the NSFastEnumeration protocol method the object prints fine, but as soon as it gets cast to id __unsafe_unretained * I lose the original Objective-C corresponding class.
To be honest I'm not quite sure how the (__unsafe_unretained id *)(__bridge void *) casting works in this case. The (__unsafe_unretained id *) seems to cast to match the right type itemsPtr needs. The (__bridge void *) seems to cast to a pointer of type void with __bridge used to bridge the obj-c world to the CF world. As per the llvm docs, for __bridge:
There is no transfer of ownership, and ARC inserts no retain operations
Is that correct?
From my understanding __NSCFConstantString is just the core foundation equivalent of NSString. I also understand that with ARC you need to bridge from Objective-C objects to CoreFoundation equivalents because ARC doesn't know how to manage the memory of the latter.
How can I get this working so that the objects in my 'for..in' loop are of the original type?
Also note that in this case I'm adding NSStrings to my collection but in theory it should support any object.
UPDATE
Rob's answer is on the right track, but to test that theory I changed the for loop to this:
for (id object in myCustomCollection) {
NSString *stringObject = (NSString *)object;
NSLog(#"String %# length: %d", stringObject, [stringObject length]);
}
In theory that should work since the objects are equivalent but it crashes with this error:
+[__NSCFConstantString length]: unrecognized selector sent to class
It almost looks like the objects returned in the for loop are classes and not instances. Something else might be wrong here... Any thoughts on this?
UPDATE 2 : SOLUTION
It's as simple as this: (thanks to CodaFi
state->itemsPtr = &someObject;
You're incorrectly casting someObject. What you meant is:
state->itemsPtr = (__unsafe_unretained id *)(__bridge void *)&someObject;
(Let's get rid of those awful casts as well)
state->itemsPtr = &someObject;
Without the address-of, your variable is shoved into the first pointer, which is dereferenced in the loop. When it's dereferenced (basically, *id), you get the underlying objc_object's isa class pointer rather than an object. That's why the debugger prints the string's value inside the enumerator call, and the class of the object inside the loop, and why sending a message to the resulting pointer throws an exception.
Your code is fine the way it is. Your debug output is revealing an implementation detail.
NSString is toll-free-bridged with CFString. This means that you can treat any NSString as a CFString, or vice versa, simply by casting the pointer to the other type.
In fact, under the hood, compile-time constant strings are instances of the type __NSCFConstantString, which is what you're seeing.
If you put #"hello" in your source code, the compiler treats it as a NSString * and compiles it into an instance of __NSCFConstantString.
If you put CFSTR("hello") in your source code, the compiler treats it as a CFStringRef and compiles it into an instance of __NSCFConstantString.
At run-time, there is no difference between these objects in memory, even though you used different syntax to create them in your source code.

Pointers on Objective-c

From what I understand (and please correct me if I'm wrong):
int x, count = 10;
int *hello;
hello = &count;
x = *hello;
Here the variables x and count are declared to be of type integer. Additionally, the variable count is assigned the value of 10.
hello is a pointer to type integer. hello is then assigned the address of count.
In order to access the value of count, hello must have an asterisk in front of it, ie, *hello.
So, x is assigned the value of whatever is in count and in this case, 10.
However...
Fraction *myFraction = [[Fraction alloc] init];
[myFraction someMethod];
Here, if I understand correctly, myFraction is a pointer to an instance of Fraction class.
myFraction is pointing to (or rather assigned the address of) an object which has been assigned memory and initialised.
Surely, in order to access the object that myFraction points to, I ought to write:
[*myFraction someMethod];
Given the way in which x accessed the value of count, surely in order to access the object, one ought to write this and not:
[myFraction someMethod];
In addition, if I have
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *str = #"Programming can be a headache";
NSLog (#"%#\n", str);
Why is str being treated as an object above? Or is str an object and in which case, why would I want to make it point to an instance of NSString class? Surely, I ought to be able to just assign an object to str?
All the usage of objects in objective c is done through object references, e.g. pointers.
The Objective C syntax lets you treat objects without dereferencing them, this is different than C / C++ for that matter.
str is an object reference of type NSString (as myFraction) and #"Programming can be a headache" is an object reference of type NSString as well, so you can assign it to str.
Additional to Binyamin
Everything inside the brackets [ ] is objective-C and not simple C (so [*object message] is not common to use)
[object message], you send the "message" to your object, and the object responds with something (he can return something or he can do something without anything in return).
Everything with a * on the left is pointer. So *str is a pointer. And where is it point? to an object NSString. The #"blabla" returns the adress of one CONSTANT string that has generated directly by the compiler.
NSLog (#"%#\n", str); here the %# calls the +Description class method of NSString object called str. By default the description of an NSString Object returns the value of the string (the text). %# is not a simple replace like the %d with numbers (int,double etc). All objects have +Description method that inherit from the NSObject (note is Class method and not instant).
description
Returns a string that represents the contents of the receiving class.
(NSString *)description

Cant store int or double in dictionary

When trying to store a double or an int in a dictionary I get an error
error: Semantic Issue: Sending 'double' to parameter of incompatible type 'id'
I have the following code
[data setValue:longitude forKey:#"longitude"];
longitude is a double.
How should I store this? should I just create a pointer to an int or a double?
As the other posts have stated, you need to use an NSNumber to wrap your double value in an object. The reason for this is that all the Cocoa foundation collection classes are designed to work with objects rather than primitive values. As you suggested, with some work you could in fact pass a pointer to a double value (I think, if you managed to cast it as an id type pointer), but as soon as your method finished and the double went out of scope it would be released and your pointer would now be pointing to garbage. With an object, the collection (NSDictionary, in this case) will retain your object when it's added and release it when it's removed or the collection is dealloc'ed, ensuring your value will survive until you don't need it anymore.
I would do it as follows:
NSNumber *tempNumber = [[NSNumber alloc] initWithDouble:longitude];
[data setValue:tempNumber forKey:#"longitude"];
[tempNumber release];
Which will leave your NSNumber object with only a +1 reference count (the dictionary retaining it) and no autoreleases
The other suggested method of doing:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
will also work fine but your object will end up with +1 reference count an an autorelease from the numberWithDouble method. When possible I try to avoid autoreleases, but the code is more concise. YMMV.
Try using an NSNumber:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
A dictionary wants an NSObject, not a number or a pointer. However it's easy to create an object containing a number:
NSNumber *mylongitudeObject = [NSNumber numberWithDouble:myLongitude];
which you can thence store in an NSDictionary.
Under the hood: mylongitudeObject will actually be a pointer to an opaque structure containing a copy of your number; but the structure also contains information so that the Objective C runtime knows what it can do with this object, such as how to copy it into a dictionary, etc.
You must use an NSNumber object instead. Try declaring longitude as follows
NSNumber longitude = [NSNumber numberWithDouble:myLongitude]
With the newer version of the compiler, you can use the Objective-C literal syntax to create a NSNumber from a variable of type double:
double longitude = 2.0;
dict[#"longitude"] = #(longitude);

How to use #encode() to get #"NSArray" in Objective-C

I'm using the runtime functions to get the type of a property (thanks to eJames for helping me to figure out this way).
The attribute string of the property looks like this:
T#"NSArray",&,Vstuff
I need to check if the property type is an array, at the moment I'm doing it like this:
- (BOOL)valueForKeyIsArray:(NSString *)key fromTagret:(id)target
{
NSString *lowerCaseKey = [self convertToKVCKey:key];
objc_property_t property = class_getProperty([target class], [lowerCaseKey UTF8String]);
NSString *propertyAttrs = [NSString stringWithUTF8String:property_getAttributes(property)];
NSString *encodedType = #"#\"NSArray\"";
NSRange range = [propertyAttrs rangeOfString:encodedType options:NSLiteralSearch];
return range.location != NSNotFound;
}
But since Apple can change the type definition string at any time, I would like to generate this #"NSArray" type string. I tried it with #encode(), but it did not work:
NSString *encodedType = [NSString stringWithUTF8String:#encode(NSArray *)];
So how can I generate this type string? Or is there a better way to check if this property attributes contain the array type?
There is no way to check this. In Objective-C source code the variables being typed as NSArray * is only there for the compiler to issue warnings. It has no meaning, and does not exist at runtime. If you mis-typed an NSArray as an NSString, you would get lots of warnings when compiling, but your code would behave exactly the same when run. At runtime all that is known is that the ivar/property is "an object".
Another way to think of it, is that once Objective-C is compiled, all object references are id references.
Just accept that if the runtime changes, your code will break, and move on. However, I think you might be miscategorizing ivars of type NSMutableArray *, CFArrayRef, or CFMutableArrayRef. You also seem to be assuming all keys correspond directly to a declared property.
The cleanest solution might be to assert that the sample object being used for the test (the target) must have a non-nil value for that key, and just grab the value and test that [[target valueForKey:key] isKindOfClass:[NSArray class]].