views:

1149

answers:

2

Style-wise (and functionally, if there is any difference), for declaring private methods, which of these is better?

@interface MyClass()

@interface MyClass(private)
+1  A: 

Yes,

there are the following differences.

1) Using anonymous categories requires implementing its methods in the main @implementation block for the corresponding class; anonymous categories allow you to declare additional required API for a class in locations other than within the primary class @interface block

2) When using MyClass(private), the following must be taken into account: object/category named pairs must be unique. If you declare a private category on your own class, then there are no problems. However, things are different on existing classes. For instance, only one NSString (Private) category can exist in a given Objective-C namespace. This can lead to problems because the Objective-C namespace is shared between the program code and all the libraries,frameworks,and plug-ins.This is especially important for Objective-C programmers writing screensavers,preference panes, and other plug-ins because their code will be injected into application or framework code that they do not control.

unforgiven
Are you sure it's the case that categories have to be uniquely named? It's my understanding that as long as the selectors are unique, there will be no conflicts. Is the runtime even aware of categories? (the runtime reference doesn't mention them)
rpetrich
It's not the category that must be uniquely named: it's the pair object/category that must.Try yourself to compile two different NSString (Private) categories, even using different methods in one of your project and see if this works.
unforgiven
Hmm... it seems that the compiler/linker doesn't allow duplicate categories on a class, but the runtime does. You can define NSDictionary(NSDictionary) or NSError(NSErrorPrivate) categories with a +load method and the compiler, linker and runtime will all behave as expected (even though Foundation.framework implements both of those categories internally). If you implement a category that's been named in a header such as NSString(NSStringExtensionMethods), the compiler will emit warnings, but the linker and runtime still behave as expected. None of this is actually useful in the real world tho.
rpetrich
Exactly, unfortunately this does not help too much...
unforgiven
I see. So would you say that an anonymous category (the first option) is better because it enforces that the methods are implemented, and it can't conflict with other categories?
Elliot
I would say that if you implement a category on your own class, then it can safely be either anonymous or named, since there can be no conflict (unless you use a class name equal to another class you are linking in your code, but if you prefix your classes with your own unique prefix this should not happen). If, instead, the category is associated to an existing class, then it should be better to avoid both anonymous and private categories, and choose a name for your category that can not conflict with existing pairs (class/category), prefixing the category name again using your own prefix.
unforgiven
Agreed, this sounds safe. However, for convention of what is actually good practice, I suggest reading bbum's answer. He's an authority on Objective-C — he's been writing in the language for over 20 years now. :-)
Quinn Taylor
+6  A: 

The two syntaxes serve different purposes.

A named category -- @interface Foo(FooCategory) -- is generally used to:

(1) extend an existing class by adding functionality. Example: NSAttributedString in Foundation is extended by a category in AppKit that adds AppKit specific RTF-like text formatting API.

(2) declare a set of methods that might or might not be implemented by a delegate. Example: Various classes declare -- but don't implement -- @interface NSObject(SetODelegateMethods).

Form (2) has fallen out of favor now that @protocol has been extended to support @optional methods in Objective-C 2.0.

A class extension -- @interface Foo() -- is designed to allow you to declare additional private API -- SPI or System Programming Interface -- that is used to implement the class innards. This typically appears at the top of the .m file. Any methods / properties declared in the class extension must be implemented in the @implementation, just like the methods/properties found in the public @interface.

Class extensions can also be used to redeclare a publicly readonly @property as readwrite prior to @synthesize'ing the accessors.

Example:

Foo.h

@interface Foo:NSObject
@property(readonly, copy) NSString *bar;
-(void) publicSaucing;
@end

Foo.m

@interface Foo()
@property(readwrite, copy) NSString *bar;
- (void) superSecretInternalSaucing;
@end

@implementation Foo
@synthesize bar;
.... must implement the two methods or compiler will warn ....
@end
bbum
Doesn't the @synthesize bar; line mean the compiler will generate the two methods (getter and setter) for bar?
Elliot
Yes, but only the getter is declared in the public header, the setter is declared privately for use inside the class itself. This is not C++, if you want to introspect the runtime you can find out the setter is there and call it where ever you want, but unless you explicitly do that you will get compile warnings if you casually try to set the property.
Louis Gerbarg
@Louis is correct. The second property overrides that the "readonly" in the .h file property by specifying "readwrite", so that's what the compiler actually does. However, only the getter is visible to people including the header file, since when they import the file they'll only see the "readonly" version of the property.
Quinn Taylor