14

I always thought that one cannot declare an object property in a category. Until my partner did it in our app's code, and it seemed to work.

I went on a SO and Google binge to try to explain to him that no, Objective-C categories can only be used to add methods, not properties. I found questions such as:

But then I found this link on Apple's site that contains the following about the @property declaration:

A property declaration begins with the keyword @property. @property can appear anywhere in the method declaration list found in the @interface of a class. @property can also appear in the declaration of a protocol or category. (emphasis added)

I know that this doesn't work:

@interface MyClass ()
NSInteger foobar;
- (void) someCategorizedMethod;
@end

But this compiles:

@interface MyClass ()
@property NSInteger foobar;
- (void) someCategorizedMethod;
@end

My question is (a) what's the best practice here? and (b) is this something that is new to Objective-C 2.0, and instead of using a "real" iVar, it simply uses associative storage behind the scenes to make this work?

Community
  • 1
  • 1
makdad
  • 6,402
  • 3
  • 31
  • 56

3 Answers3

31

You have always been able to declare an @property in a category. What you couldn't do -- and still can't -- is declare storage for the property in the category, neither as an instance variable nor via `@synthesize.

However....

@interface MyClass () is not a category. It is a class extension and has a distinctly more specific role than a category.

Namely, a class extension can be used to extend a class's @interface, and this includes @properties that can be @synthesized (including synthesizing storage in the modern runtime).

Foo.h:

@interface Foo
@end

Foo.m:

@interface Foo()
@property int x;
@end

@implementation Foo
@synthesize x; // synthesizes methods & storage
@end

it simply uses associative storage behind the scenes to make this work?

Nope -- it is a real instance variable. The modern runtime fixes the fragile base class problem.


@interface MyClass ()
NSInteger foobar;
- (void) someCategorizedMethod;
@end

The above doesn't work (as expected) because foobar is, effectively, a global variable.

If you change it to:

@interface MyClass () {
    NSInteger foobar;
}
- (void) someCategorizedMethod;
@end

Then it'll work with the latest release of the llvm compiler (with the right flags, as @Joshua indicated in a comment).

bbum
  • 162,346
  • 23
  • 271
  • 359
  • To add to this, his first example where he adds an iVar is perfectly valid in llvm with -Xclang -fobjc-nonfragile-abi2 – Joshua Weinberg Dec 27 '10 at 17:43
  • 1
    It would be if he were adding an ivar. In that case, he is adding a static variable that is not an instance variable. – bbum Dec 27 '10 at 17:49
  • thank you for the extensive explanation. I'll be sharing this link with my coding partners!! – makdad Dec 27 '10 at 19:11
1

Assotiative storage is the solution. Have a look at this post.

Basem Saadawy
  • 1,808
  • 2
  • 20
  • 30
1

Generally speaking, properties are nothing different from other methods. As long as the ivar used is available in the ordinary class, there is no problem at all. It's just syntactic sugar.

Things start to get more difficult if also the ivar is automatically created, as is possible in some configurations.

The main point here is that declaration of the ivar is independent from the property.

Eiko
  • 25,601
  • 15
  • 56
  • 71