Instance variable declared in an @interface block instead of a property
ID |
objectivec.maintainability.property_data_member |
Severity |
low |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Encapsulation |
Language |
Objective-C |
Description
Reports an @interface - a class declaration or a class extension - that declares instance variables in a brace block, where a property declaration would do. Both brace placements are recognised, on the @interface line and on the line below it. An interface with no brace block, and an empty brace block, are not reported. One finding is reported per interface, at the @interface line.
Rationale
An instance variable in the interface block is raw storage: it has no accessors, so nothing can be validated, observed or made thread-safe at the point of access, and every read and write in the class touches the field directly. It is also part of the declaration rather than the implementation, so adding, removing or reordering one changes the layout of the class - the reason Apple moved ivar declarations into the implementation and made the compiler synthesise them from properties. A property gives the same storage a name that key-value observing, copy semantics and atomic can all attach to.
The following code illustrates the pattern detected by this rule:
// A class extension declared in the implementation file carries the same block.
// FLAGGED: Instance variable declared in an @interface block instead of a property
@interface Widget () {
NSMutableArray *_pending;
}
@end
// FLAGGED: Instance variable declared in an @interface block instead of a property
@interface Widget : NSObject {
NSInteger _count;
NSString *_title;
}
@property (nonatomic, copy) NSString *name;
@end
/*
@interface Commented : NSObject {
NSInteger _old;
}
@end
*/
@implementation Widget
- (void)work {
NSInteger local = 1;
_count = local;
NSLog(@"%@ %@", _title, _pending);
}
@end
Remediation
Declare a property instead - @property (nonatomic, copy) NSString *title; - and let the compiler synthesise the backing ivar; the underscore-prefixed name stays available inside the implementation. Where the storage is genuinely private, put the property in a class extension in the implementation file rather than in the public header.