74

If i want to extend a class like AVAudioPlayer, whats the best way to also add another method to AVAudioPlayerDelegate?

Should I make a category for it, do I extend it?

If I extend it do I then also have to make sure to overwrite the actual delegate getter/setter? How would I extend the protocol?

The following gives me errors

@protocol AudioTrackDelegate : AVAudioPlayerDelegate {
    - (void)foo;
}
@end

@interface AudioTrack : AVAudioPlayer {
}
@end
pkamb
  • 33,281
  • 23
  • 160
  • 191
dizy
  • 7,951
  • 10
  • 53
  • 54

2 Answers2

134

The syntax for creating a protocol that implements another protocol is as such:

@protocol NewProtocol <OldProtocol>
- (void)foo;
@end

If you want to call a method in NewProtocol on a pointer typed as OldProtocol you can either call respondsToSelector:

if ([object respondsToSelector:@selector(foo)])
    [(id)object foo];

Or define stub methods as a category on NSObject:

@interface NSObject (NewProtocol)
- (void)foo;
@end
@implementation NSObject (NewProtocol)
- (void)foo
{
}
@end
rpetrich
  • 32,196
  • 6
  • 66
  • 89
  • If I create a protocol that implements another protocol... Is there anything else I need to do to make sure the 'OldProtocol' methods get fired ? because for me i'm getting the new protocol firing but all the existing ones aren't any longer – dizy Apr 09 '09 at 05:35
  • 10
    A protocols doesn't have methods, it has method definitions; that is, it describes the name, arguments and return types that a class must have to be deemed "conforming" to said protocol. By your comment/question I'm betting you are forgetting to call [super setDelegate:value] in your -setDelegate: – rpetrich Apr 09 '09 at 22:38
  • using this approach you doesn't exposes the new protocol's methods, take a look on my reply for a cleanest way – Kappe Jan 17 '14 at 16:31
  • @simonthumper: In what way is it horrible? – rpetrich Aug 11 '15 at 17:47
  • You're adding methods via a category that are nothing to do with NSObject to a system class which is used everywhere. It's not going to break your code of course, but it is hacky, also every single object you access you will be able to call those methods on, which is going to make code completion fun times! – simonthumper Aug 12 '15 at 07:51
  • @simonthumper: It's a common pattern known as an informal protocol. See, for example Apple's CALayerDelegate and UIAccessibility categories on NSObject. – rpetrich Aug 12 '15 at 16:05
10

Remember a protocol adds no code to the compiled app -- it only enforces the fact that your class must implement the methods to be considered "conforming" to the protocol. A good use of this would be to generate a group of classes with all the same way of operating: <printable> or <serialized>, etc. So you could create a <plays> protocol for instance:

@protocol plays
    - (void) play;
    - (NSString *) type;
@end

And then a class that conforms to <plays> MUST implement the play and type methods. If it doesn't, the compiler issues a warning but compiles the class anyway. In your code you check if an object does conform to a protocol with the following code:

if ([obj conformsTo: @protocol(plays)]) {
    [obj play];
}

A category actually adds new methods dynamically to your class. These methods are globally accessible to the runtime as selectors and can be called by name as in @selector(foo) and [object foo:bar];

The purpose of a category is to add special new code to a class even if you don't have the source code for that class. There may be security problems and you could create memory leaks in classes, etc.

In your case maybe, in a separate file AVAudioPlayerDelegate_TrackOps.m

#import "AVAudioPlayerDelegate.h"
@implementation AVAudioPlayerDelegate (TrackOps)

- (NSObject *) foo {
    // do foo stuff;
    return bar;
}

@end

Putting it as a category of NSObject makes all classes respond to foo. Foo can be a stand alone method Objc_perform_selector(@selector(foo)) as well.

Bottom Line: use a category to add a quick method to a class, protocols for enforcing method implementations, and subclasses to specialize existing classes (things like adding member variables or major new functionality). Categories can also be used to override a method or two when a subclass is not needed and wanted, but usually if you want to add functionality to a class you make a subclass. For more examples, ideas, other general information on this topic, there's always the Apple introduction to Objective-C

Matt
  • 9,068
  • 12
  • 64
  • 84
  • Can you please elaborate on how to add a category to a protocol ? I think somethings wrong with the syntax of what you wrote. thanks – dizy Apr 10 '09 at 04:22