Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hiding properties from public access

Tags:

objective-c

I'm trying to declare properties that are for internal use only in a Private category as such:

@interface BarLayer (Private)

@property (readwrite, retain) MenuItemFont  *menuButton;
@property (readwrite, retain) Menu          *menuMenu;
@property (readwrite, retain) LabelAtlas    *messageLabel;

@end

Now I'm trying to figure out where exactly I'm supposed to @synthesize those.

I tried:

@implementation BarLayer (Private)

@synthesize menuButton      = _menuButton;
@synthesize menuMenu        = _menuMenu;
@synthesize messageLabel    = _messageLabel;

@end

Here, the compiler complains:

@synthesize not allowed in a category's implementation

So I tried putting it in my BarLayer implementation, but here it doesn't find the declarations in the BarLayer interface.

no declaration of property ‘menuButton’ found in the interface

What would the correct way be?

like image 861
lhunath Avatar asked Apr 13 '09 10:04

lhunath


2 Answers

You can't use @synthesize with a category.

You can do this with an class extension (a.k.a. anonymous category), which is just a category without a name whose methods must be implemented in the main @implementation block for that class. For your code, just change "(Private)" to "()" and use @synthesize in the main @implementation block along with the rest of your code for that class.

See the Apple docs on extensions for more about that. (Apparently this is new in Mac OS 10.5.)

EDIT: An example:

// Main interface (in .h)
@interface Foo : NSObject
- (void)bar;
@end

// Private interface (in .m, or private .h)
@interface Foo ()
@property (nonatomic, copy) NSString *myData;
@end

@implementation Foo
@synthesize myData; // only needed for Xcode 4.3 and earlier
- (void)bar { ... }
@end

Another solution, which is much more work, is to use objc_setAssociatedObject and objc_getAssociatedObject to fake additional instance variables. In this case, you could declare these as properties, and implement the setters and getters yourself using the objc_* runtime methods above. For more detail on those functions, see the Apple docs on Objective-C runtime.

like image 126
Jesse Rusak Avatar answered Dec 02 '22 06:12

Jesse Rusak


I found an explanation of why synthesis of properties is prohibited in categories, but how you can use class extensions instead:

The following information comes from http://www.friday.com/bbum/2009/09/11/class-extensions-explained/

"The reason synthesis was prohibited in categories was because synthesis requires storage and there was no way of declaring storage in a category efficiently and it wasn’t deemed acceptable to allow a category to synthesize methods that would then diddle the class’s ivars directly. Too fragile and ugly.

However, it was also obviously desirable to be able to declare a property that was publicly readonly, but whose implementation was readwrite for internal-to-the-class-or-framework purposes.

One additional requirement is that the synthesis such properties must always be able to synthesize both the setter and getter naturally and precisely. Specifically, when declaring a property as atomic, there is no way the developer can correctly manually write only 1/2 of the getter setter pair; the locking infrastructure is not exposed and, thus, there is no way to guarantee atomicity in such a situation.

Class extensions addressed this problem elegantly.

Specifically, you can declare a property like:

@interface MyClass : NSObject
@property(readonly) NSView *targetView;
@end

And, then, in the implementation file:

@interface MyClass()
@property(readwrite) NSView *targetView;
@end

@implementation MyClass
@synthesize targetView;
@end

End result? A property that is publicly readonly, but privately readwrite without opening properties up to all of the fun fragility associated with categories."

like image 28
Rose Perrone Avatar answered Dec 02 '22 06:12

Rose Perrone