object_getInstanceVariable works for float, int, bool, but not for double? - objective-c

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.

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"];

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.

Related

pass pointer to method in Objective-C

Merry Christmas everybody :)
I have a pointer problem. Although I´m familiar with pointer concepts I haven´t used pointers in Objective-C so far the way it´s described here.
I modified it like this:
int countSInteger = 10;
[self setHMSValues:countSInteger];
- (void) setHMSValues: (int*) timeCat {
*timeCat = *timeCat - 1;
}
But now I´receiving a EXC_BAD_ACCESS:
Any Santa hints?
Greetings from Switzerland, Ronald Hofmann
It looks like you want setHMSValues: to calculate and return a value for the integer parameter. However, the parameter is a pointer to an int (int *), and you're passing a plain int with the value of 10. Because pointers are just integer values themselves (with the integer value representing a memory address), the code is trying to set the value at memory location 10; hence, you get a "bad access" error because your program cannot access or change values at memory location 10.
What you should do is pass the address of countSInteger to the method:
[self setHMSValues:&countSInteger];
However, there's a better way to do this. Since you're returning only one value from the method, there's no need for an out parameter. You can change your method to this:
- (int) setHMSValues: (int) timeCat {
return timeCat - 1;
}
and call it like this:
int countSInteger = 10;
countSInteger = [self setHMSValues:countSInteger];
Try:
- (void) setHMSValues: (int*) timeCat {
*timeCat = *timeCat - 1;
}
"countInteger" is declared as an "int *" while the method you're calling into is expecting an "int".
Don't you think you might have better luck if you declare "countInteger" as a plain "int"?
Why use int pointers? Just remove the *
Does Objective-C support pass by reference using & like C++? I haven't checked Objective-C specifically, (never had the need) but in C++ the following is used to tell the compiler to use pass-by-reference:
void count(int &var);

How to check if a variable is an object?

Is there any way to do the following at compile-time?
int anInteger = 0;
__if_object(anInteger) {
// send object some messages
}
__if_primitive(anInteger) {
// do something else
}
An dummy situation where this could be used is to define the __add_macro below.
#define __add_macro(var, val) __something_goes_here__
int i = 1;
MyInteger* num = [[MyNumber alloc] initWithValue:1]
__add_macro(i, 4);
__add_macro(num, 4);
// both should now hold 5
Clarification/Simplification
I guess there is no way to do this with one macro. But I still need it to warn if the macro is being used on the wrong datatype. Those two types are: object and non-object).
To check if it is an object, this works:
#define __warn_if_not_object(var) if(0){[(var) class];}
What I need:
#define _warn_if_object(var) if(0){__something_here__}
Again, I need this to happen at compile-time. And it can either throw an error or warning.
Thanks
When you declare an int variable you can really only put an int value in it.
While this is Objective-C, and hence C, so you can bypass just about every type protection mechanism that exists, this is not to be advised. Indeed there is no guarantee whatsoever that a, say, NSNumber reference will even fit into an int variable - and more than enough chance that if you try, and bypass any warnings, some bits will just get tossed making the reference invalid.
So, no, while you can tell what class an object reference refers to, you cannot in general tell whether a variable has an integer value or an object reference in it - you shouldn't even try to put these two very different things into the same variable.
Answer 2
Patrick, your comments and clarification seem to suggest you are not trying to do what the question starts out by asking (how do you determine if the value in an int is an object - answered above, you don't), but something rather different...
I think what you're after is function overloading, and as you seem to be trying to use macros, maybe inline functions as well. Clang supports function overloading, here is program fragment which may show you how to solve your problem:
// Clang likes prototypes so let's give it some
// The following declares two overloaded inline functions:
NS_INLINE void __attribute__((overloadable)) byType(int x);
NS_INLINE void __attribute__((overloadable)) byType(NSNumber *x);
// now some simple definitions:
NS_INLINE void __attribute__((overloadable)) byType(int x)
{
NSLog(#"int version called: %d", x);
}
NS_INLINE void __attribute__((overloadable)) byType(NSNumber *x)
{
NSLog(#"NSNumber version called: %#", x);
}
// now call them, automatically selecting the right function
// based on the argument type
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
int x = 5;
NSNumber *y = [NSNumber numberWithInt:42];
byType(x);
byType(y);
}
The above code when run outputs:
int version called: 5
NSNumber version called: 42
Clang 3 compiles the above code inlining the two calls, so you get the same code as using macros.
please don't mix between scalar values and pointers to objects... it will not end well.
if you insist you can do something with Objective-C++
something like
int sum(int,int);
NSNumber * sum(NSNumber *, NSNumber *);

Objective-C accessing float getters with variable names

Let's say I have an NSArray called myArray of NSStrings (#"a0",#"a1",#"a2")
Then in a fast enumeration I loop into my array to build properties according to that NSStrings. I've got a problem accessing that properties.
I'm trying something like that :
#property (nonatomic) float a0propertyLow;
#property (nonatomic) float a0propertyHigh;
#property (nonatomic) float a1propertyLow;
#property (nonatomic) float a1propertyHigh;
..
.. etc.
for (NSString *aPos in myArray) {
NSString *low = [NSString stringWithFormat:#"%#propertyLow",aPos];
NSString *high = [NSString stringWithFormat:#"%#propertyHigh",aPos];
SEL lowSel = NSSelectorFromString(low);
SEL highSel = NSSelectorFromString(high);
if ([self respondsToSelector:lowSel]&&[self respondsToSelector:highSel]) {
id sumPartOne = [self performSelector:lowSel];
id sumPartTwo = [self performSelector:highSel];
float bla = (float)sumPartOne + (float)sumPartTwo;
}
}
I know my code is wrong but I don't know how to make it work.
My problem is that lowSel and highSel are getters which returns float but the perform selector method returns id which is ok for an object but not for floats.
So, how can I access my float getters with variable names ? I'm sure answer must be simple but it seems that my mind is looking for something complicated (and which obviously doesn't work) so I'm asking for help :)
Thank you very much for your help
You can't use performSelector: to call a method that returns a scalar value. The documentation for performSelector: clearly says what you have to do:
For methods that return anything other than an object, use NSInvocation.
An NSInvocation is a little more complex to set up but more flexible regarding arguments and return types.
In your case, it is probably easier to use Key-Value Coding instead:
[self valueForKey:low];
takes the return type into account and will automatically wrap the float in an NSNumber.
If you really need to use these getter methods, you can change your properties to double and use objc_msgSend_fpret():
#include <objc/runtime.h>
#include <objc/message.h>
double arg0 = objc_msgSend_fpret(self, lowSel);
If you can avoid getters (I know, that's not good practice, but anyway, it works for sure with the following method), and use the instance variables directly:
void *object_getIvarPtr(id obj, const char *name)
{
if (!obj || !name)
{
return NULL;
}
Ivar ivar = object_getInstanceVariable(obj, name, NULL);
if (!ivar)
{
return NULL;
}
return ((char *)obj + ivar_getOffset(ivar));
}
float arg0 = *(float *)object_getIvarPtr(self, [lowSel UTF8String]);
Hope this helps.
One way you can do is convert your floats into objects at runtime such as:-
NSString *str=[NSSTring stringWithFormat:#"%f",yourFloatValue];
and then u can retrive it using
[str floatValue];

Is it safe to use pointers to change values in blocks?

I've started using blocks, and one of the first things I encountered is an inability to set values which are captured by the closure. This is fine, I've been using C/C++ a long time. I'll just use pointers!
MyObject* bestObj = nil;
float bestDist= 10000.f;
MyObject **pBestObj = &bestObj;
float* pBestDist = &bestDist;
[self testObjects:class block:^(MyObject* obj){
CGRect r = [obj boundingBox];
// position is captured from outside this code sample
if( CGRectContainsPoint( r, position ) )
{
float dist = GetDistance( obj, position );
if(dist < bestDist)
{
*pBestDist = dist;
*pBestObj = obj;
}
}
}];
return bestObj;
My question is, is this safe? I assume that as long as my pointer points to something that hasn't gone out of scope and that still exists, that it should work. But I'm also assuming that things that take blocks don't ever, say, run them in parallel. I know my code doesn't, but I don't know about, say, using a block with an NSArray enumerateObjectsUsingBlock call.
The 'right' way to do this would be to mark those original variables as block mutable, __block

Function to swap pointers in Objective-C

- (void) swapController:(MyViewController*)controller1
with:(MyViewController*)controller2
{
MyViewController *swap = controller2;
controller2 = controller1;
controller1 = swap;
}
Looks like this doesn't work because I'm not passing references. How to do it anyway?
You can do this by passing in pointers to the pointers you want to change:
- (void)swapController:(MyViewController**)controller1 with:(MyViewController**)controller2
{
MyViewController* swap = *controller2;
*controller2 = *controller1;
*controller1 = swap;
}
Not looking for this as an opposing answer here, but rather to complement #Huw's answer.
On implementing his swapController:with: method above, I noticed some issues with ARC and with primitives. ARC will take issue if you're passing a strong id* to an id* ( which it will designate as autoreleased id and other fun warnings).
If you're anything like me, well, one fine day you'll be swapping pointers back and forth in objc and one of those values will be a primitive data type.
If you do end up doing something like that, here's a little helper (like I said earlier) to complement the above answer:
void swapObjcPointerWithC ( void** ptrA, void** ptrB ) {
void *temp = *ptrA;
*ptrA = *ptrB;
*ptrB = temp;
}
...
// Later on
id objA;
int anInt = 10;
swapObjcPointerWithC ( (void*)&objA, (void*)&anInt );
There you go. My two cents.
Note: also works will nil pointers apparently.