1

I'am a newby in objective c and iphone developing. I confused. I trying to create button that are created at the runtime,after clicking another button,and application doesn't know it:

 -(void)button4Pushed{
    NSLog(@"Button 4 pushed\n");
    Class cls = NSClassFromString(@"UIButton");//if exists {define class},else cls=nil
    id pushButton5 = [[cls alloc] init];

    CGRect rect =CGRectMake(20,220,280,30);
    NSValue *rectValue = [NSValue valueWithCGRect:rect];


    //--------------1st try set frame  - work,but appears at wrong place
    //[pushButton5 performSelector:@selector(setFrame:) withObject:rectValue];
    //--------------2nd try set frame  + this work correctly
    [pushButton5 setFrame: CGRectMake(20,220,280,30)];                    



    //this work correct [pushButton5 performSelector:@selector(setTitle:forState:) withObject:@"5th Button created by 4th" withObject:UIControlStateNormal];
    //but i need to use invocation to pass different parameters:

    NSMethodSignature *msignature;
    NSInvocation *anInvocation;

    msignature = [pushButton5 methodSignatureForSelector:@selector(setTitle:forState:)];
    anInvocation = [NSInvocation invocationWithMethodSignature:msignature];

    [anInvocation setTarget:pushButton5];
    [anInvocation setSelector:@selector(setTitle:forState:)];

    NSNumber* uicsn =[NSNumber numberWithUnsignedInt:UIControlStateNormal];
    NSString *buttonTitle = @"5thbutton";

    [anInvocation setArgument:&buttonTitle atIndex:2];
    [anInvocation setArgument:&uicsn atIndex:3];
    [anInvocation retainArguments];
    [anInvocation invoke];

    [self.view addSubview:(UIButton*)pushButton5];
}

What am I doing wrong? Invocation is invoked, but there is no result... I know that I can create it this way:

    UIButton *pushButton3 = [[UIButton alloc] init];
    [pushButton3 setFrame: CGRectMake(20, 140, 280, 30)];
    [pushButton3 setTitle:@"I'm 3rd button!created by 2nd" forState:UIControlStateNormal];
    [self.view addSubview:pushButton3];

But I need to use invocation, and don't know why does it not working?

Thanks for your help.

Alexander
  • 1,228
  • 2
  • 15
  • 29

2 Answers2

2

Your setting an NSNumber * as your second argument, but the method you are trying to invoke (effectively) requires an int. Use your exact same code but try these lines instead:

UIControlState uicsn = UIControlStateNormal;

// then

[anInvocation setArgument:&uicsn atIndex:3];
Perception
  • 79,279
  • 19
  • 185
  • 195
  • You are right. Thanks a lot. I used NSNumber to wrap the UIControlState,because I actually need to pass it to NSArray, and then retrieve it frome NSValue. I've spend 2 days,but I can't understand how to do this. – Alexander Aug 11 '11 at 09:51
  • @Alexander - no problem, glad it's working for you now. Don't know if you solved the other problem yet, but you can always wrap and store the UIControlState ***after*** the NSInvocation. In any case, good luck and don't forget to accept whatever answer you think helped you out the most! – Perception Aug 11 '11 at 10:40
  • Perception,with guys help the problem of passing UIControlState to function by wrapping it, was solved. You can read about it [here](http://stackoverflow.com/questions/7036665/nsvaluegetvalue-strange-behavior-why-this-happens) – Alexander Aug 15 '11 at 07:07
1

Why do you need to use invocation?

Update: Based on the use case, unless you don't control the other classes, I would instead use a protocol with a method matching this signature and collect objects on which calling this is legal.

NSInvocation is a runtime building block/last-resort class; you are supposed to use the other tools available to you, like protocols (if an object has a method) and Blocks or function pointers (if you just want the disembodied function) if you can at all control the surrounding objects.

Perception's answer solves the technical problem, but you might be making things a lot more complicated for yourself.

Jesper
  • 7,477
  • 4
  • 40
  • 57
  • 1
    Please do not post answers containing questions. Add a comment to the question instead. – DarkDust Aug 10 '11 at 11:53
  • @DarkDust: I was planning on updating it with an answer once Alexander commented. Sadly I got sidetracked. – Jesper Aug 10 '11 at 17:36
  • @Jesper: Still, on StackOverflow we have the "rule" (etiquette) that [answers should be answers](http://meta.stackexchange.com/questions/17447/answer-or-comment-whats-the-etiquette). Your request for clarification ("why do you need to use invocation") and its answer is of interest to everyone who wants to provide an answer and thus should be attached directly to the question as comment. So please post a comment next time and then provide an answer. Thanks. – DarkDust Aug 10 '11 at 19:02
  • @Jesper,I want to write function that get folowing params:id object,NSString methodName,NSArray* params,(id)resObj. And for object it will do any method with passed params,result write to resObj,and return if function was executed or not. performSelector can have only 3 params (withObject:). And this can be done only by invocations,because it have setArgument: method,which I can use in for-loop. – Alexander Aug 15 '11 at 03:37
  • @Alexander: You don't *need* NSInvocation for that, you can just use objc_msgSend. I agree that NSInvocation is a bit easier in this case if you're starting from scratch. But I still think that the problem could be solved in a much easier way if you change the code to use protocols or blocks. – Jesper Aug 16 '11 at 08:04
  • @Jasper: I thought about it,but I can't find out how to pass to `obj_msgSend` multiple params, number of which I don't know. And using NSInvocation I can add them dynamically using `setArgument:atIndex:`. Though, the obj_msgSend works faster,doesn't it? – Alexander Aug 17 '11 at 08:32
  • @Alexander: NSInvocation calls objc_msgSend behind the scenes. It looks to me like you know exactly how many parameters you have. What you have to do is to call `objc_msgSend(, , )`. In your case, that'd be `objc_msgSend(pushButton5, @selector(setTitle:forState:), buttonTitle, UIControlStateNormal)`. For that case in particular, I don't see why you wouldn't just use `[pushButton5 setTitle:buttonTitle forState:UIControlStateNormal]`. – Jesper Aug 17 '11 at 08:57