May 27, 2009

Private properties for iPhone Objective-C

Keeping members private is extremely important in Object Oriented design as it allows us to adhere to the principles of encapsulation and information hiding, and makes it easier to keep our objects thread safe. We often want to use @properties in Objective-C to take care of retain counts in setters for us. However, unless you are careful these properties will always be public. This 'kitchen sink' approach results in all of your internal object state being exposed on your public API. This is dangerous because:
  • Developers can modify the internal state of your objects when they shouldn't
  • Developers are 'tempted' to access things that they shouldn't as they can see 'apparently useful' things on the interface.
  • Developers are confused when they read the interface - they need to somehow work out which public properties they can use and which they can't.
In short: NEVER expose class internals publicly - it is very bad OO practice.

Private setters are discussed in Scott Stevenson's Objective-C tutorial, but the post does not explain how to make both the setter and getter private - furthermore I was tripped up by a coupled of points that are not illustrated in Scott's examples. So how do we create completely private properties in Objective-C? The steps are as follows:
  1. Make the member variable @private. This is not essential to create the property but if we're making the property private then it'd be wise to make the member variable accessible only from within our class.
  2. DO NOT declare the property in the main @interface in the header (.h) file
  3. Create an @interface for an extension (unnamed category) in the .m file ABOVE the @implementation
  4. Declare your private @property in the extension interface
  5. @synthesize the private property in your @implementation as normal.
See code below for an example:

ClassWithPrivateProperty.h

    @interface ClassWithPrivateProperty : NSObject {
    @private
        NSString* member;
    }
    - (void) trySettingPrivateProperty;
    @end

ClassWithPrivateProperty.m

    #import "ClassWithPrivateProperty.h"
 
    @interface ClassWithPrivateProperty ()
    @property (nonatomic,retain) NSString* member; 
    @end
 
    @implementation ClassWithPrivateProperty
    @synthesize member;
    - (void) trySettingPrivateProperty {
        self.member = @"A Value";
        NSLog(@"myClass.member = %@", self.member);
    }
    @end

17 comments:

  1. Cool post, explained exactly what I was trying to do. Thanks!

    ReplyDelete
  2. thanks, that's exactly what I was looking for :)

    ReplyDelete
  3. Two additional points:

    (1) In the 64 bit Mac OS X runtime and the iPhone/iPad runtime, there is no need to declare the ivar as @synthesize will synthesize the ivar, when needed.

    (2) You can make a property public readonly and privately readwrite by declaring the property 'readonly' in your @interface and then repeating the declaration in your class extension, replacing 'readonly' with 'readwrite'.

    ReplyDelete
  4. This is good practice, however be aware that the dynamic nature of Objective-C means there is no such thing as a truly private method. At runtime, any object can query the messages your class understands and send any of those messages. For a definitive discussion, see this post by bbum on Stack Overflow (apologies if the link gets mangled):
    http://stackoverflow.com/questions/2158660/why-doesnt-objective-c-support-private-methods/2159027#2159027

    ReplyDelete
  5. Thanks Robert. The same could be said of Java - you can actually access private members and methods on a class using reflection if the security manager allows.

    Whether this is a problem depends on your motivations for using a private scope. Personally I create private properties to clearly show my intent - i.e: 'accessing this property outside of this class is not meaningful, may not result in expected behaviour etc.' It's a statement to other developers who may use my code - they can clearly see the things they are expected to call. They are free to subvert this - but the process of doing so serves as a warning.

    ReplyDelete
  6. Like so many others, this was exactly what I was looking for, thanks!

    ReplyDelete
  7. Thanks for the tip !
    Anyway, I was wondering whether it was possible to have a member variable X with a public readonly property and a private readwrite property for the same X member variable.
    Currently I am explicitly writing setters methods in @implementation part. It actually works with dot notation (which is quite logical given that it uses key/value pattern), but I was wondering if there was an easier solution ?

    ReplyDelete
  8. Actually, the example also works without defining "NSString* member;" field in "ClassWithPrivateProperty.h". Looks like @property in enough to define field. Anyone knows why is that?

    ReplyDelete
  9. Woo it helps and I just know "unnamed category" is special category for property definition.

    @Damien Rambout: Just add one line in ClassWithPrivateProperty.h for method interface:
    - (NSString*) member;

    ReplyDelete
  10. Thanks a lot, very helpful

    ReplyDelete
  11. Damien,

    Yes, that is possible. In your interface file, declare the property as readonly, and in the implementation file, declare it as readwrite in the extension.

    ReplyDelete
  12. Wait.
    What about if I want to synthetise them (to avoid releasing and retain manually) but not a public access though setters and getters?
    I tried moving synthesize to private section, but it didn't work.

    Any suggestion?
    Thanks.

    ReplyDelete
  13. Ricardo, they are synthesized and the setters/getters are private - not publicly accessible.

    ReplyDelete
  14. Thanks for the great article! I was especially finding articles about how you can use @private and @property in more of a OO fashion, and this is the exact post I was looking for :)

    ReplyDelete