views:

318

answers:

4

I have three classes which implement the same protocol, and have the same parent class which doesn't implement the protocol. Normally I would have the protocol as pure virtual functions in the parent class but I couldn't find an Objective-C way to do that.

I want to utilize polymorphism on these subclasses by declaring pure virtual functions in the superclass then have the children implement those functions. However the only Objective-C way I've found to do this is to have each child explicitly decide to implement a protocol, when I do it this way the superclass doesn't know the children will implement that protocol so there are compile time warnings all over the place.

Some pseudo-code if that didn't make sense:

@interface superclass: NSObject
{}

@interface child1: superclass<MyProtocol>
{}

@interface child2: superclass<MyProtocol>
{}

The consumer of these classes:

@class child1
@class child2
@class superclass

@interface SomeViewController: UIViewController
{
    child1 *oneView;
    child2 *otherView;
    superclass *currentView;
}

-(void) someMethod
{
    [currentView protocolFunction];
}

The only nice way I've found to do pure virtual functions in Objective-C is a hack by putting [self doesNotRecognizeSelector:_cmd]; in the parent class, but it isn't ideal since it will cause runtime errors rather than compile time.

+2  A: 

Objective-C developers commonly use dynamic checking rather than compile-time checking in these situations because the language and the frameworks support it so well. So for example, you could write your method like this:

- (void)someMethod
{
    // See if the object in currentView conforms to MyProtocol
    //
    if ([currentView conformsToProtocol:@protocol(MyProtocol)])
    {
        // Cast currentView to the protocol, since we checked to make
        // sure it conforms to it. This keeps the compiler happy.
        //
        [(SuperClass<MyProtocol> *) currentView protocolMethod];
    }
}
jlehr
Of course, it's more reliable to use `respondsToSelector:@selector(protocolMethod)`.
KennyTM
...and necessary if the protocol has optional methods.
jlehr
A: 

Personally, I would implement the protocol on the super class, but implement the methods like this:

- (id) myProtocolMethod {
  NSAssert(NO, [NSString stringWithFormat:@"-[%@ %@] must be overridden", NSStringFromClass([self class]), NSStringFromSelector(_cmd)]);
  return nil;
}

That way if you ever forget to override a method in a concrete subclass, it should be immediately obvious.

Dave DeLong
It depends whether all subclasses should conform to the protocol; not really sure what Winder has in mind there. But I'm definitely not putting those asserts in my production code. I'd rather just provide empty methods, and maybe log a warning.
jlehr
In fact, I was going to suggest sort of the opposite: consider making the protocol methods optional, and do the runtime check, but again, it really depends what the intent is.
jlehr
This is the behavior I want, just with a compiler error rather than a runtime one. It doesn't look like Objective-C has a way to do this though.
Winder
A: 

Alternatively you can use the following

if ([unknownObject conformsToProtocol:@protocol(MyProtocol)])
   [unknownObject performSelector:@selector(methodInProtocol)];

instead of the following if you just want to suppress the warning.

if ([unknownObject conformsToProtocol:@protocol(MyProtocol)])
   [unknownObject methodInProtocol];  // will cause warning

performSelector: will only work if the number of arguments is zero or one. More flexible invocations can be achieved with NSInvocation.

NSSplendid
+1  A: 

I was able to get the compiler to warn me correctly by making the superclass *currentView property look like this:

@property (nonatomic, retain) superclass<MyProtocol> *currentView;
Winder