views:

572

answers:

3

I've got object_getInstanceVariable to work as here however it seems to only work for floats, bools and ints not doubles. I do suspect I'm doing something wrong but I've been going in circles with this.

float myFloatValue;
float someFloat = 2.123f;
object_getInstanceVariable(self, "someFloat", (void*)&myFloatValue);

works, and myFloatValue = 2.123

but when I try

double myDoubleValue;
double someDouble = 2.123f;
object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);

i get myDoubleValue = 0. If I try to set myDoubleValue before the function eg. double myDoubleValue = 1.2f, the value is unchanged when I read it after the object_getInstanceVariable call. setting myIntValue to some other value before the getinstancevar function above returns 2 as it should, ie. it has been changed.

then I tried Ivar tmpIvar = object_getInstanceVariable(self, "someDouble", (void*)&myDoubleValue);

if i do ivar_getName(tmpIvar) i get "someDouble", but myDoubuleValue = 0 still! then i try ivar_getTypeEncoding(tmpIvar) and i get "d" as it should be.

So to summarize, if typeEncoding = float, it works, if it is a double, the result is not set but it correctly reads the variable and the return value (Ivar) is also correct.

I must be doing something basic wrong that I cant see so I'd appreciate if someone could point it out.

+1  A: 

What about using valueForKey:?

NSNumber * value = [self valueForKey:[NSString stringWithUTF8String:ivar_getName(tmpIvar)]];
NSLog(@"Double value: %f", [value doubleValue];

Note: this requires you to have a "someFloat" method. If you want to use setValue:forKey:, you'll also need the "setSomeFloat:" method. This is easily implemented by declaring the ivar as an @property and synthesizing it.

Dave DeLong
yup that works, didnt know about valueForKey:, though I'm still interested why the getInstanceVariable didnt work.
Russel West
+5  A: 

object_getInstanceVariable is a confused little function. It is documented that the last parameter is a void ** parameter—that is, you pass the address of a void * variable and get a pointer to the instance variable—but it is implemented as if it was a void * parameter—that is, you pass the address of the variable that you want to hold a copy of the instance variable. The problem is that the implementation ignores the size of the instance variable and just does a pointer copy. So anything that's the same size as a pointer will work perfectly. If you're running on a 32-bit architecture, only the high 32 bits will be copied. (You should witness the same behavior with a long long instance variable as well.)

The solution is to use the primary API, key-value coding, using -valueForKey:.

The other solution: If you wanted to write a fixed version, say as a category for NSObject, it would look something like this:

@implementation NSObject (InstanceVariableForKey)

- (void *)instanceVariableForKey:(NSString *)aKey {
    if (aKey) {
        Ivar ivar = object_getInstanceVariable(self, [aKey UTF8String], NULL);
        if (ivar) {
            return (void *)((char *)self + ivar_getOffset(ivar));
        }
    }
    return NULL;
}

@end

Then your code would look like this:

double myDoubleValue = *(double *)[self instanceVariableForKey:@"someDouble"];
John Calsbeek
So thats why! I wasted an hour trying to figure out what was going on! Good explanation too.
Russel West
The obj variable is never defined in the method above should actually be self, correct?
wbyoung
Er, yes, you are absolutely correct.
John Calsbeek
A: 

I know this is an old question. but i have just tested this and was wondering where the obj param is coming from?

Could you let me know? thanks

eaymon