What's wrong with using a category on NSObject

2019-02-21 18:20发布

问题:

I've been looking for a way to use optional protocol methods and have clean code. In other words:
1: No respondsToSelector: calls all over my code
2. Should work for any method signature, so a category method on NSObject making the check and calling performSelector: is out (and NSInvocation has problems cooperating with ARC)
3: This solution, IMO, pretends to be universal but has all the drawbacks of 1

I eventually came up with this idea:

@protocol MyProtocol <NSObject>
@optional
-(void)optionalMethod;
@end

@interface ClassA : NSObject <MyProtocol>
@end

@implementation ClassA

-(void)optionalMethod{
     NSLog(@"ClassA implements optionalMethod");
}

@end

@interface ClassB : NSObject <MyProtocol>
@end

@implementation ClassB
//classB does not implement optionalMethod
@end

@interface NSObject (DefaultMyProtocolImplementation)
-(void)optionalMethod;
@end

@implementation NSObject (DefaultMyProtocolImplementation)
-(void)optionalMethod{
     NSLog(@"%@ does not implement optionalMethod", NSStringFromClass([self class]));
}
@end

It seems to work, i.e.:

...
ClassA *objA = [[ClassA alloc] init];
ClassB *objB = [[ClassB alloc] init];

[objA optionalMethod]; //prints "ClassA implements optionalMethod"
[objB optionalMethod]; //prints "ClassB does not implement optionalMethod"

While many places online discuss this problem, I haven't stumbled upon this solution, which makes me think there's something wrong with it -- some major case where it will fail, or be unpredictable.

Should I just do it, or are my concerns valid?

回答1:

Methods added to existing system classes should be prefixed somehow. I.e. exec_myMethod or exec_doSomethingToThis:. So, your solution is in violation of that.

Beyond that, it also means that a class cannot opt out of whatever the default @optional method's behavior might be (which is basically nothing because your default implementation really should be a no-op).

So, no, overall, there isn't something horrendously wrong with providing a default implementation beyond the violation of the should add prefix rule for adding methods via category to existing classes. But that isn't a hard rule.

The other downside is that you are polluting the method namespace. This will be a disadvantage during development in that Xcode will code complete all the methods, easily avoided by simply not exposing the declarations (which don't need to be exposed). At runtime, it means that respondsToSelector: isn't useful for these methods, but that is kind of by design.

Still... it smells to this old timer's code olfactory center.