Question

I have the following design :

@protocol SomeProtocol <NSObject>

@optional
- (void)someMethod;

@end

@interface SomeObject : NSObject <SomeProtocol>
@end

I want to provide a default implement of someMethod for my protocol so I make a category like so :

@interface SomeObject (SomeCategory) <SomeProtocol>

- (void)someMethod;

@end

@implementation SomeObject (SomeCategory)

- (void)someMethod
{
    // default implementation for all classes implementing the protocol SomeProtocol
}

@end

Now I also want to leave the possibility to implement a custom implementation of this method. If I implement directly on the SomeObject class like so :

@interface SomeObject <SomeProtocol>

- (void)someMethod;

@end

@implentation SomeObject

- (void)someMethod
{
    // custom implementation for SomeObject class
}

@end

This implementation seems to be prioritized over the category method. But Apple documentation says :

If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.

I did not think yet about other possibilities which could be better (like working with a base class and subclasses maybe) but is what I'm doing here a good design or will it necessary lead to undefined behavior ?

Thanks in advance for your help

Was it helpful?

Solution

The documentation is not quite clear on the matter. Apart from what has been mentioned so far, at some other point in the docs, it says: "When a category overrides an inherited method, the method in the category can, as usual, invoke the inherited implementation via a message to super. However, if a category overrides a method that already existed in the category's class, there is no way to invoke the original implementation" - which I read as "the category implementation will always run instead of the original implementation".

I've read this before but didn't find it just now, however it has been mentioned in this SO answer.

Personally, I've been frequently overwriting methods using categories and so far, it has caused me no trouble. However since I know I'd "kill" the original implementation, I'm careful about doing it only with methods for which it is safe to assume that the class is inheriting the original implementation from a superclass.

For example, I'd consider it safe to override touchesBegan:withEvent: in a category on UIView, since that method has been inherited from UIResponder and the original implementation remains untouched and can (and should) be reached by calling super from within the implementation of the category.

To my knowledge, abstract or empty implementations are safe to override, too. For example, I've been overriding awakeFromNib in UIScrollView to overcome the problem with the scrollview not setting its contentSize property according to the actual content size on its own, in case it has been created in Interface Builder:

@implementation UIScrollView (MyHandyCategory)

-(void)awakeFromNib {

   [self autoResizeContent];

}

-(void)autoResizeContent {

   NSArray *subViews = [self subviews];
   UIView *contentView = [subViews objectAtIndex:0];
   [self setContentSize:contentView.frame.size];

}

@end

In case you wonder why I didn't just put the autoresize code in awakeFromNib - I wanted to be able to call autoresize on other occasions as well. In my actual category, there is more code in awakeFromNib which I don't necessarily want to run along with the autoresize code.

OTHER TIPS

Well, since Apple says the the behavior is undefined, then I would think it's a bad idea. It might work today but there is not guarantee that it will work tomorrow.

I'm not sure there is a great option for what you want to do but a better one would be to look into swizzling.

This allows you to replace a selector on one class with another. Here is a good intro into what it can do and some areas to watch out for - https://stackoverflow.com/a/8636521/3096507.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top