The @property and @synthesize directives

Let's face it, writing getters and setters is usually a useless task. In a typical Java program, you're required to do something like this:

  1. ...
  2. private int hatSize;
  3.  
  4. public int getHatSize() {
  5. return hatSize;
  6. }
  7.  
  8. public void setHatSize(int hatSize) {
  9. this.hatSize = hatSize;
  10. }
  11. ...

That seems like a lot of unnecessary code to simply manage the hatSize property. And although we want to maintain data encapsulation, it would be great if the language could auto-generate the content for us. (And as you've guessed, Objective-C provides this capability through the @property and @synthesize directives.)

In this example, we'll replace the code for managing the property named "level" with auto-generated code.

The updated interface file:

  1. #import <Foundation/Foundation.h>
  2.  
  3. @interface GasCan : NSObject
  4.  
  5. @property float level;
  6.  
  7. -(id) initWithSize: (int)x;
  8. -(id) init;
  9. -(void) pourContents;
  10. -(int) size;
  11. -(void)openSpout: (BOOL)x andValve: (BOOL)y;
  12. @end

Pay attention to line 5: Instead of defining level as a standard float, we've used the @property directive.

And the updated implementation file:

  1. #import "GasCan.h"
  2.  
  3. @implementation GasCan {
  4. @protected
  5. int size;
  6. @private
  7. BOOL spoutOpen;
  8. BOOL valveOpen;
  9. }
  10.  
  11. @synthesize level;
  12.  
  13. // Call the designated initializer and default the can to a size of 5
  14. -(id) init {
  15. return [self initWithSize:5];
  16. }
  17.  
  18. // The designated initializer
  19. -(id) initWithSize: (int)x {
  20. self = [super init];
  21. if (self) {
  22. size = x;
  23. }
  24. return self;
  25. }
  26.  
  27. -(void) pourContents {
  28. if (!spoutOpen) {
  29. NSLog(@"You can't pour the contents because the spout is not open.");
  30. } else {
  31. float increment = (valveOpen) ? .1 : .01;
  32. while (level > 0.0) {
  33. NSLog(@"Glug glug glug");
  34. level -= increment;
  35. }
  36. level = 0;
  37. }
  38. }
  39.  
  40. //-(float) level {
  41. // return level;
  42. //}
  43.  
  44. //-(void)setLevel: (float)l {
  45. // level = (l > size) ? size : l;
  46. //}
  47.  
  48. -(int) size {
  49. return size;
  50. }
  51.  
  52. -(void)openSpout: (BOOL)x andValve: (BOOL)y {
  53. spoutOpen = x;
  54. valveOpen = y;
  55. }
  56.  
  57. @end

On line 11 of this file, I've used the @synthesize directive to automatically create a getter / setter pair. (As evidence of this, I've commented out the getter / setter code on lines 40-46)

Compiling the program and running it produces the same results. However, if you look at line 45, there's no longer a check to ensure the level increases beyond the size. In the next example, I'll combine the auto-generated getter with a custom setter.