Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Since when is it possible to declare Objective-C 2.0 properties in a category?

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:

  • Setting New Property In Category Interface Implementation (look at the accepted answer)
  • Can I add a property for a method not in my category?

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?

like image 770
makdad Avatar asked Dec 27 '10 16:12

makdad


1 Answers

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).

like image 199
bbum Avatar answered Oct 12 '22 23:10

bbum