In addition to the approach suggested by HangarRash i would consider two other options:
- Extending
routeContext
property itself in the class extension:
@interface TDWObject ()
@property (copy, nonatomic, nonnull) NSMutableArray<NSString *> *routeContext;
@end
- Just introducing ivar in the class extension for the property manually (and accessing it directly in the implementation):
@interface TDWObject () {
NSMutableArray<NSString *> *_routeContext;
}
@end
Personally I would prefer the manual ivar due to the following reasons:
- It doesn't introduce any redundant methods that clang would synthesise otherwise (you neither need extra getter, nor setter for a
NSMutableArray *
)
- It's the most performant (accessing ivar directly).
I would also recommend to alter the property attributes as follows:
// The header file
@interface TDWObject : NSObject
@property (copy, readonly, nonatomic, nonnull) NSArray<NSString *> *routeContext;
@end
Here a couple of clarifications regarding the properties choice:
copy
storage - technically for a readonly
property storage attrbitue should not make much difference, because it predominantly denotes setter semantic (if we don't count that the value for this property can also be passed as a constructor argument). However, in our case the getter is custom and returns a copy of the internal object (instead of just reference to it). If you look through Cocoa/Cocoa Touch API, they often use copy
attribute when they want to explicitly say that you deal with copies of internal data structure and any changes made to the instance obtained from the property wouldn't be tracked by the owning object. (e.g. -[NSCharacterSet invertedSet]
), thus it's an important part of the interface description. Why the copy is needed at all? because otherwise the client code can easily exploit the mutability of the original data, and manage its content itself.
nonatomic
atomicity - first, I don't know if there is a reason to make the property atomic, and you commonly use nonatomic
properties by default (because they don't have burden of synchronisation, which slows down access/read performance). Second - clang would not be able to pair a synthesized setter with a user defined getter (if you choose to use properties approaches instead of ivar). Last, but not least - since getter is user-defined, you will have to manage the synchronisation yourself, so it doesn't come "for free".
Finally, the implementation part would look like this:
@implementation TDWObject
#pragma mark Lifecycle
- (instancetype)init {
if (self = [super init]) {
_routeContext = [NSMutableArray array];
}
return self;
}
#pragma mark Actions
- (NSArray<NSString *> *)routeContext {
return [_routeContext copy];
}
- (void)addFoo {
[_routeContext addObject:@"Foo"];
}
@end