2

I have a class with a function that needs to be called from inside and outside the class. The next code works fine but I was wondering, is there a way to have only one lowerKeyboard method instead of two methods with - and + ? If i'll keep just the + method I'll get an error unrecognized selector sent to instance when trying to call the method from inside the class

From inside the class:

-(void)someOtherMethod
{
    UIBarButtonItem *infoButtonItem=[[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone  target:self action:@selector(lowerKeyboard)];
}

from outside the class:

[myClass lowerKeyboard];

myClass:

-(void)lowerKeyboard
{
    //do something

}

+(void)lowerKeyboard
{
        //do the exact same thing
}
Segev
  • 19,035
  • 12
  • 80
  • 152
  • possible duplicate of [What is the difference between class and instance methods?](http://stackoverflow.com/questions/1053592/what-is-the-difference-between-class-and-instance-methods) – David Snabel-Caunt Oct 01 '13 at 14:15
  • @DavidCaunt You look like tod from breaking bad! I wasn't asking for the difference, i know the difference. My question asks how to combine the two. – Segev Oct 01 '13 at 14:19
  • Nah, he looks like me! – David Snabel-Caunt Oct 01 '13 at 14:24
  • If you (a) need the class method; but (b) also need the instance method for the purposes of the `@selector` of your `UIBarButtonItem`, then you probably need both methods. I might have the instance method actually call the class method (technically less efficient, but makes the intent more clear). – Rob Oct 01 '13 at 14:28
  • One wonders if you couldn't simply code `target:[self class]` above? (Haven't tried it.) – Hot Licks Oct 01 '13 at 15:24
  • @HotLicks Tried that, I'm getting a warning saying "instance class method not found" – Segev Oct 01 '13 at 15:56
  • Sounds like at least part of the problem is that the compiler can't form the selector in the usual way. Might be worth looking at the other ways you can get a selector. – Hot Licks Oct 01 '13 at 18:48

2 Answers2

3

Say you have the following:

- (void)doFoo
{
  NSLog(@"Foo");
}

+ (void)doFoo
{
  NSLog(@"Foo");
}

You can refactor this to either do both implementations like so:

- (void)doFoo
{
  [[self class] doFoo];
}

+ (void)doFoo
{
  NSLog(@"Do Foo!");
}

However, it is worth pointing out that having two similarly named methods like this is asking for trouble. You'd be far better off removing one of the two interfaces to avoid confusion (especially as you only need one copy of the implementation anyway!).

Bad advice follows - do not actually do this unless you really know how to mess with the run-time (I don't.)

Technically, you can duplicate a class implementation and an instance implementation by editing the run-time like so:

// Set this to the desired class:
Class theClass = nil;
IMP classImplementation = class_getImplementation(class_getClassMethod(theClass, @selector(doFoo)));
class_replaceMethod(theClass, @selector(doFoo), classImplementation, NULL)

This should ensure that calling +[theClass doFoo] calls exactly the same implementation as calling -[theClass doFoo]. It completely removes the original instance implementation from the class's implementation stack (so handle with a lot of caution). I can't think of any really legitimate cases for doing so however, so treat this with a pinch of salt!

David Doyle
  • 1,716
  • 10
  • 23
  • How can I set the selector to that method? Should I just set it to another method and that method should call [[self class] doFoo]; ? If i'll set the selector to doFoo i'll still get `unrecognized selector sent to instance` – Segev Oct 01 '13 at 14:24
  • @Sha David's suggestion is that you should have the instance method, precisely to avoid this "unrecognized selector". And I like the approach of the instance method calling the class method far better than either the first approach (duplicating the code) or, yikes, the runtime replacement of the method. – Rob Oct 01 '13 at 14:34
  • 1
    @DavidDoyle +1, but, no offense, I kind of wish you stopped short of even suggesting that `class_replaceMethod` approach. It's academically interesting that you can do that, but, wow, I agree, what a really bad idea! lol – Rob Oct 01 '13 at 14:38
0
-(void)lowerKeyboard
{
    //this can be called on class instance

    //UIBarButtonItem *infoButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"Done" style:UIBarButtonItemStyleDone  target:self action:@selector(lowerKeyboard)];
    //[infoButtonItem lowerKeyboard];
}

+(void)lowerKeyboard
{
    //this can be used as class static method
    //you cannot use any class properties here or "self"

    //[UIBarButtonItem lowerKeyboard];
}
Grzegorz Krukowski
  • 18,081
  • 5
  • 50
  • 71