Objective‑C Properties: @property, @synthesize, and @dynamic
Objective‑C Properties: @property, @synthesize, and @dynamic
@property
Declaring a property instructs the compiler to generate an accessor pair and a backing instance variable (ivar) when needed.
Example:
@interface MyViewController : UIViewController
@property (nonatomic, assign, getter=isReady) BOOL ready;
@end
The above can be expanded (conceptually) into an ivar plus explicit setter/getter:
@interface MyViewController : UIViewController {
BOOL _ready;
}
- (void)setReady:(BOOL)value;
- (BOOL)isReady;
@end
@implementation MyViewController
- (void)setReady:(BOOL)value {
_ready = value;
}
- (BOOL)isReady {
return _ready;
}
@end
When you omit explicit accessor implementations, modern compielrs auto‑synthesize them. By default, the auto‑synthesized ivar is named with a leading underscore: _propertyName.
@synthesize
@synthesize controls how accessors are generated and which ivar they use.
- Default mapping
When you write @synthesize without specifying a backing ivar, the compiler creates an ivar that matches the property name exactly (no underscore) and wires accessors to it.
@interface StatusController : NSObject
@property (nonatomic, assign) BOOL active;
@end
@implementation StatusController
@synthesize active; // generates ivar named `active`
@end
Equivalent manual form:
@interface StatusController : NSObject {
BOOL active;
}
- (void)setActive:(BOOL)flag;
- (BOOL)active;
@end
@implementation StatusController
- (void)setActive:(BOOL)flag { active = flag; }
- (BOOL)active { return active; }
@end
Important: With @synthesize active; the ivar is active (no underscore). This differs from auto‑synthesis when @synthesize is omitted, where the compiler creates _active by default.
- Custom ivar mapping
You can explicitly bind a property to any ivar you choose.
@interface Metrics : NSObject {
BOOL backingFlag;
}
@property (nonatomic, assign) BOOL enabled;
@end
@implementation Metrics
@synthesize enabled = backingFlag;
@end
Equivalent manual form:
@interface Metrics : NSObject {
BOOL backingFlag;
}
- (void)setEnabled:(BOOL)v;
- (BOOL)enabled;
@end
@implementation Metrics
- (void)setEnabled:(BOOL)v { backingFlag = v; }
- (BOOL)enabled { return backingFlag; }
@end
The common underscore pattern is also supported:
@interface Store : NSObject
@property (nonatomic, assign) BOOL loaded;
@end
@implementation Store
@synthesize loaded = _loaded; // accessors read/write `_loaded`
@end
Auto‑synthesis and naming
- If you declare a property and do not provide accessors or an @synthesize/@dynamic, the compiler auto‑synthesizes accessors and an ivar named _propertyName.
- If you write @synthesize propertyName; explicitly, the ivar becomes propertyName (no underscore) unless you map it: @synthesize propertyName = someIvar;
Legacy behavior
Before Xcode 4.4 (clang 3.1/LLVM 4.0 era), you typically wrote both an ivar and @synthesize property = _property; explicitly. Auto‑synthesis was introduced later, making that pattern the default unless overridden.
A subtle pitfal
Mixing an explicit @synthesize with a separately declared underscore ivar often leads to logic errors.
@interface Player : NSObject {
BOOL _muted; // declared but not used by accessors below
}
@property (nonatomic, assign, getter=isMuted) BOOL muted;
@end
@implementation Player
@synthesize muted; // creates ivar `muted` (no underscore)
- (void)startPlayback {
self.muted = YES; // writes to `muted`
if (_muted) { // checks `_muted`, which is still NO
[self applySilentMode];
}
}
- (void)applySilentMode { /* ... */ }
@end
Here, _muted and muted are different ivars. Avoid declaring a parallel underscore ivar when using @synthesize property; or map explicitly: @synthesize muted = _muted;.
@dynamic
@dynamic tells the compiler that accessor methods will be supplied at runtime and should not be auto‑synthesized. It asserts "don’t generate ivars or accessors; they’ll appear dynamically."
Typical with Core Data:
@interface Person : NSManagedObject
@property (nullable, nonatomic, copy) NSString *name;
@end
@implementation Person
@dynamic name; // NSManagedObject provides accessors via the runtime
@end
Advanced uses include adding accessor implementations at runtime (e.g., via class_addMethod) or handling them through message forwarding. In ordinary app code, @dynamic is uncommon compared to auto‑synthesized properties.