Protocols

Some languages, such as C++, permit multiple inheritance. Objective-C does not support multiple inheritance, but it takes a safer approach by supporting protocols.

For Java developers, protocols are very similar to interfaces: A protocol is defined in a header file, and then implemented in an actual class. However, unlike Java, some of the methods in a protocol can be optional.

If an object supports all of the required components of a Protocol, then it "conforms" to that Protocol. This can be checked by using the 'conformsToProtocol' method.

In this example, I'll implement a Protocol named 'LiquidContainer'. (And since a marine gas can is a container of liquid, it will implement the protocol.)

The LiquidContainer protocol:

  1. #import <Foundation/Foundation.h>
  2.  
  3. @protocol LiquidContainer
  4.  
  5. -(float) maxCapacity;
  6. -(NSArray *) liquidsSupported;
  7. -(void) disposeOfContents;
  8. @optional
  9. -(float) containerVolume;
  10.  
  11. @end

The marine gas can interface file:

  1. #import "GasCan.h"
  2. #import "LiquidContainer.h"
  3.  
  4. enum engineType { twoStroke, fourStroke };
  5.  
  6. @interface MarineGasCan : GasCan <LiquidContainer>
  7. @property float oilLevel;
  8. -(id) initWithSize: (int)x andType: (enum engineType)y;
  9. -(float) getOilToGasRatio;
  10. @end

The marine gas can implementation file:

  1. #import "MarineGasCan.h"
  2. #import "GasCan.h"
  3.  
  4. @implementation MarineGasCan {
  5. enum engineType canType;
  6. }
  7. @synthesize oilLevel;
  8.  
  9.  
  10. -(float) maxCapacity {
  11. return self.size;
  12. }
  13.  
  14. -(NSArray *) liquidsSupported {
  15. NSArray *liquids = [NSArray arrayWithObjects: @"Diesel", @"Gas", @"Marine Gas", nil];
  16. return liquids;
  17. }
  18.  
  19. -(void) disposeOfContents {
  20. super.level = 0;
  21. }
  22.  
  23. -(float) containerVolume {
  24. return 9.9;
  25. }
  26.  
  27. // Call the designated initializer and default the can to a size of 5
  28. -(id) init {
  29. return [self initWithSize:3 andType: fourStroke];
  30. }
  31.  
  32.  
  33. -(id) initWithSize: (int)x andType: (enum engineType)y {
  34. self = [super initWithSize:x];
  35. if (self) {
  36. canType = y;
  37. }
  38. return self;
  39. }
  40.  
  41.  
  42. -(void) pourContents {
  43. if (!spoutOpen) {
  44. NSLog(@"You can't pour the contents because the spout is not open.");
  45. } else {
  46. if ((canType == twoStroke) && (oilLevel == 0)) {
  47. NSLog(@"WARNING: This is a 2-stroke gas can, but there's no oil!");
  48. } else if ((canType == fourStroke) && (oilLevel > 0)) {
  49. NSLog(@"WARNING: This is a 4-stroke gas can, but there's oil in it!");
  50. }
  51. // Pour more slowly than a generic gas can, since we're probably on the water
  52. // float increment = (valveOpen) ? .1 : .01;
  53. float increment = (valveOpen) ? .09 : .005;
  54. while ([super level] > 0.0) {
  55. NSLog(@"Glug glug glug");
  56. [super setLevel: [super level] - increment];
  57. }
  58. super.level = 0;
  59. }
  60. }
  61.  
  62. -(float) getOilToGasRatio {
  63. if (self.level == 0) {
  64. return 0;
  65. }
  66. return oilLevel / self.level;
  67. }
  68.  
  69. @end

The main.m file:

  1. #include "GasCan.h"
  2. #include "MarineGasCan.h"
  3.  
  4. int main(int argc, char * argv[]) {
  5. @autoreleasepool {
  6. MarineGasCan *gCan = [[MarineGasCan alloc] init];
  7. [gCan setLevel:3];
  8.  
  9. if ([gCan conformsToProtocol: @protocol(LiquidContainer)]) {
  10.  
  11. NSArray *sLiquids = [gCan liquidsSupported];
  12. for(NSString *curLiquid in sLiquids) {
  13. NSLog(@"Liquid: %@", curLiquid);
  14. }
  15.  
  16. [gCan disposeOfContents];
  17. }
  18.  
  19. NSString *message = @"The level in your gas can is: ";
  20. NSLog(@"%@ %f",message, gCan.level);
  21. }
  22. }