3

I try to pass CGRect to NSInvocation (setArgument:atIndex:). I wrap it by NSValue, push to the NSArry,then get from NSArray and use NSValue (getValue:). Calling of (getValue:) method causes the changing of before declared index NSInteger i. Can anybody say why does this happen?

 NSString className = @"UIButton";
    Class cls = NSClassFromString(className);
    cls pushButton5 = [[cls alloc] init];
    CGRect rect =CGRectMake(20,220,280,30);
    NSMethodSignature *msignature1;
    NSInvocation *anInvocation1;

    msignature1 = [pushButton5 methodSignatureForSelector:@selector(setFrame:)];
    anInvocation1 = [NSInvocation invocationWithMethodSignature:msignature1];

[anInvocation1 setTarget:pushButton5];
[anInvocation1 setSelector:@selector(setFrame:)];
NSValue* rectValue = [NSValue valueWithCGRect:rect];
NSArray *params1;
params1= [NSArray arrayWithObjects:rectValue,nil];
id currentVal = [params1 objectAtIndex:0];
NSInteger i=2;
if ([currentVal isKindOfClass:[NSValue class]]) {
    void *bufferForValue;
    [currentVal getValue:&bufferForValue];
    [anInvocation1 setArgument:&bufferForValue atIndex:i];
}else {
    [anInvocation1 setArgument:&currentVal atIndex:i];
}
[anInvocation1 invoke];

When this(getValue:) method implements value of 'i' changes from 2 to something like : 1130102784, and in (setArgument:atIndex:) I have an SIGABRT because index i is out of bounds.

So why does [NSValue getValue:(*void) buffer] change other variables?

(P.S. I do it in function so I simplified an example and initialize array directly. And if I set directly atIndex:2, It works perfect.But as I've sad I simplified a little, and I need to pass i to atIndex:)


Thanks to Tom Dalling(especially) and sergio problem solved. I delete this:

void *bufferForValue;
[currentVal getValue:&bufferForValue];
[anInvocation1 setArgument:&bufferForValue atIndex:i];

and paste this

NSUInteger bufferSize = 0;
NSGetSizeAndAlignment([currentVal objCType], &bufferSize, NULL);
void* buffer = malloc(bufferSize);
[currentVal getValue:buffer];
[anInvocation1 setArgument:buffer atIndex:i];

Thank you. Stackoverflow.com really helpful site with smart people.

Alexander
  • 1,228
  • 2
  • 15
  • 29

2 Answers2

10

You can't fit an NSRect into a void*. The correct code would be:

NSRect buffer;
[currentVal getValue:&buffer];

Or alternatively, if you don't know the contents of the NSValue object:

NSUInteger bufferSize = 0;
NSGetSizeAndAlignment([currentVal objCType], &bufferSize, NULL);
void* buffer = malloc(bufferSize);
[currentVal getValue:buffer]; //notice the lack of '&'
//do something here
free(buffer);

The value for i is changing because your code causes an overflow into other stack-allocated variables.

Tom Dalling
  • 23,305
  • 6
  • 62
  • 80
  • Thanks Tom.You are right,but unfortunately,it solves my problem only partially,because actually I don't know the types of parameters at NSArray. – Alexander Aug 12 '11 at 07:40
  • I haven't tried it, but apparently you can get the size of an NSValue value with this: http://forums.macnn.com/79/developer-center/131643/nsvalue-data-size-programmatically/ – Tom Dalling Aug 12 '11 at 07:43
  • Tom!You are my savior! Thanks a lot, now it works correctly, and no any stack overwriting. You really helped me. Thanks again. – Alexander Aug 12 '11 at 08:32
  • Thanks its helpme, but how I can convert the void* buffer to a C++ class? – ademar111190 Nov 01 '12 at 14:50
  • Read about `dynamic_cast<#type#>(#expression#)` or `static_cast<#type#>(#expression#)`. – Alexander Jan 28 '13 at 04:14
3

In:

 void *bufferForValue;
 [currentVal getValue:&bufferForValue];

you are copying currentVal value to a buffer which has the size of void* (a pointer); now, see getValue reference:

getValue:

Copies the receiver’s value into a given buffer.

buffer

A buffer into which to copy the receiver's value. buffer must be large enough to hold the value.

Since the value you copying over to the buffer cannot fit in a void*, you are thus overwriting your stack, since you are writing to the address of a local variable; this has likely the effect of overwriting the i local variable, so that explains what is happening.

Community
  • 1
  • 1
sergio
  • 68,819
  • 11
  • 102
  • 123
  • 2
    `bufferForValue` is uninitialised, but `&bufferForValue` is a valid pointer to stack allocated memory. His problem is that `sizeof(void*)` is smaller than `sizeof(NSRect)`. – Tom Dalling Aug 12 '11 at 07:19
  • @Tom Dalling: you are right, thanks. I removed reference to uninitialized buffer from my answer. +1 for your answer. – sergio Aug 12 '11 at 07:26