0

I have the following classes/protocols:

@interface Super
@end
@implementation Super
@end
@interface Sub1
@end
@implementation Sub1
@end
@interface Sub2
@end
@implementation Sub2
@end

@protocol FooProtocol
- (void) foo;
@end
@interface Sub1 (FooCategory) <FooProtocol>
@end
@implementation Sub1 (FooCategory)
- (void) foo {}
@end
@interface Sub2 (FooCategory) <FooProtocol>
@end
@implementation Sub2 (FooCategory)
- (void) foo {}
@end
@interface Super (FooCategory) <FooProtocol>
@end
// Notice that there is no @implementation Super (FooCategory)

This allows me to write a function similar to this:

void callFoo(NSArray *supers) {
  for (Super *theSuper in supers) {
    [theSuper foo];
  }
}

Sub1 and Sub2 are Super's only two subclasses. I essentially want polymorphism in a category method. If I specify an @interface for Super, but provide no @implementation, clang doesn't give me any warnings/errors.

Is this a really bad hack?

What are the potential downsides?

Heath Borders
  • 30,998
  • 16
  • 147
  • 256

2 Answers2

2

(Originally from a comment on zneak's answer.)

As explained in The Objective-C Programming Language, a method in a category truly becomes part of the targeted class at runtime – which is why it can be dangerous to clobber an existing method, and why the behavior of multiple categories implementing the same method is undefined. Once the method is installed, it's really no different from something that was there initially:

Category methods can do anything that methods defined in the class proper can do. At runtime, there’s no difference. The methods the category adds to the class are inherited by all the class’s subclasses, just like other methods.

It's okay to have a category on Super and categories on its subclasses implementing the same method, and it'll work exactly like if they were part of the classes themselves. (Though I do recommend following zneak's advice of implementing a stub method on Super to help with debugging if it ever gets invoked accidentally.)

Justin Spahr-Summers
  • 16,893
  • 2
  • 61
  • 79
1

Objective-C has no concept of an 'abstract method'. When you need one, you pretty much have to choice but to use some hack.

Although I'd recommend you put the method in Super and throw an exception if it's called.

zneak
  • 134,922
  • 42
  • 253
  • 328
  • I realize that Objective-C has no abstract methods. I thought that objective-C didn't allow you to override methods with a category. Apple's documentation on categories [1] says, "There’s no limit to the number of categories that you can add to a class, but each category name must be different, and each should declare and define a different set of methods." [1] http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html – Heath Borders Jun 22 '12 at 18:49
  • I can't put the method in `Super`. I don't control `Super`'s source, so I need the method to exist in a category on `Super`. – Heath Borders Jun 22 '12 at 18:50
  • @HeathBorders, then I'm fairly sure this is okay. A lot of frameworks I've seen declare categories on `NSObject`, give them null implementations, and then let classes override these methods. – zneak Jun 22 '12 at 19:07
  • That's different. Overriding a category method with a subclass is ok. Overriding any method with a category is not a good idea because of lack of `super` definition [1]. Overriding a category method with another category is explicitly unsupported (based on my link above), but I want to know whether I'm allowed to do it in a subclass. [1] http://stackoverflow.com/q/1085479/9636 – Heath Borders Jun 23 '12 at 04:07
  • @JustinSpahr-Summers Thanks for the input. If you formulate it into an answer (preferably with links to documentation and/or emails from Apple) I'll mark it as correct. – Heath Borders Jul 02 '12 at 03:19