问题
I need your help. I have some problems with NSInvocation 'getReturnValue:' method. I want to create UIButton programmatically, and even more, I want to create it dynamically using NSInvocation and through passing value through the NSArray (that's why I wrapped UIButtonTypeRoundedRect).
Listing.
NSLog(@"Button 4 pushed\n");//this code executed when button pushed
Class cls = NSClassFromString(@"UIButton");//if exists {define class},else cls=nil
SEL msel = @selector(buttonWithType:);
//id pushButton5 = [cls performSelector:msel withObject:UIButtonTypeRoundedRect];//this code works correctly,but I want to do this by NSInvocation
//---------------------------
NSMethodSignature *msignatureTMP;
NSInvocation *anInvocationTMP;
msignatureTMP = [cls methodSignatureForSelector:msel];
anInvocationTMP = [NSInvocation invocationWithMethodSignature:msignatureTMP];
[anInvocationTMP setTarget:cls];
[anInvocationTMP setSelector:msel];
UIButtonType uibt_ = UIButtonTypeRoundedRect;
NSNumber *uibt = [NSNumber numberWithUnsignedInt:uibt_];
NSArray *paramsTMP;
paramsTMP= [NSArray arrayWithObjects:uibt,nil];
id currentValTMP = [paramsTMP objectAtIndex:0];//getParam from NSArray
NSInteger i=2;
void* bufferTMP;
//if kind of NSValue unwrapp it.
if ([currentValTMP isKindOfClass:[NSValue class]]) {
NSUInteger bufferSize = 0;
NSGetSizeAndAlignment([currentValTMP objCType], &bufferSize, NULL);
bufferTMP = malloc(bufferSize);
[currentValTMP getValue:bufferTMP];//copy currentVal to bufer
[anInvocationTMP setArgument:bufferTMP atIndex:i];// The +2 represents the (self) and (cmd) offsets
}else {
[anInvocationTMP setArgument:¤tValTMP atIndex:i];//Again,+2 represents (self) and (cmd) offsets
}
void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];
NSLog(@"sizeof(UIButton)=%i,sizeof(result)=%i,methodreturnlength = %i,sizeof(*result)=%i",class_getInstanceSize(NSClassFromString(@"UIButton")),sizeof(result),[[cls methodSignatureForSelector:msel] methodReturnLength],sizeof(*result));
id pushButton5;
pushButton5=result;
//---------------------------
NSLog output: sizeof(UIButton)=140,sizeof(result)=4,methodreturnlength = 4,sizeof(*result)=1
The problem is that value from NSInvocation is pointer of size 4 bytes. It should point to UIButton object,size of 140 bytes. But actually refers to 1 byte data. So what does happen with UIButton object,that should be initialized by 'buttonWithType:'?
Added after getting some answers:
To clarify: I want to get UIButton
object, but after this code id pushButton5 = (id) result;
,when I try to work with pushButton5
,it causes EXC_BAD_ACCESS
. Can someone help me?
Can this happen because of this?
Class cls = NSClassFromString(@"UIButton");
...
[anInvocationTMP setTarget:cls];
It is correct, isn't it?
回答1:
result
has type void*
and your sizeof(*result)
expression is measuing sizeof(void)
, which apparently yields 1 in your compiler.
To check type of an Objective-C object, use isKindOfClass:
method:
id resObj = (id)result;
if ([resObj isKindOfClass:[UIButton class]])
NSLog(@"mazel tov, it's a button");
Just be sure it's really an objective-c object first.
回答2:
If you're using ARC, I would replace this:
void* result = malloc([[cls methodSignatureForSelector:msel] methodReturnLength]);
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];
With this:
CFTypeRef result;
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:&result];
if (result)
CFRetain(result);
UIButton *pushButton5 = (__bridge_transfer UIButton *)result;
The reason is that the invokation's return object is not retained, so it will go away, even if you immediately assign it to an object reference, unless you first retain it and then tell ARC to transfer ownership.
回答3:
The return value is a UIButton* not a UIButton. Thus everything looks fine in your code.
回答4:
It's not a problem.
First, getReturnValue:
should come after the invocation. So,
[anInvocationTMP getReturnValue:result];
[anInvocationTMP invoke];
should be
[anInvocationTMP invoke];
[anInvocationTMP getReturnValue:result];
Next, you should forget about the size of UIButton
itself. What buttonWithType
returns is UIButton*
, not UIButton
. In Objective-C, you should never directly deal with the object itself. You should always work with a pointer to the object.
Finally, (Objective-)C's sizeof
operator is purely compile-time operation. So, sizeof(*result)
doesn't know at all what result
points to at the run time. But it doesn't matter... you shouldn't care about the size of UIButton
, as I already told you.
回答5:
Really answer that I needed was... How do you think where ? Yes, in documentation.
Use the NSMethodSignature method methodReturnLength to determine the size needed for buffer :
NSUInteger length = [[myInvocation methodSignature] methodReturnLength]; buffer = (void *)malloc(length); [invocation getReturnValue:buffer];
When the return value is an object, pass a pointer to the variable (or memory) into which the object should be placed :
id anObject; NSArray *anArray; [invocation1 getReturnValue:&anObject]; [invocation2 getReturnValue:&anArray];
So the problem solved. Thanks for your respond guys.
来源:https://stackoverflow.com/questions/7078109/why-does-nsinvocation-getreturnvalue-lose-object