3
votes

In Objective-C, it's common to declare NSString/NSArray/NSDictionary as copy, is it necessary to do that for a readonly property or there is no difference? If an NSString is readonly, it will never be set, so declaring it strong or copy will have the same effect right?

//use strong rather copy here and it will work the same since it will never be copied?

@property (nonatomic, readonly) NSString *string;

3

3 Answers

5
votes

If it really is read-only then you don't need to specify it. If you're going to redeclare it privately to be readwrite then you do want to specify it. For readonly properties it has no effect as no setter will be created.

0
votes

You're right, but there're some things to consider. That's okay as long, as your property is immutable object. However, it is not always true.

First example, which I run into frequently, is when you have actually mutable object inside of your implementation. Like property declared NSArray in implementation can actually be NSMutableArray. Strong reference property getter for it will return pointer to that NSMutableArray. And, at some point, you'll run into situation when you request NSArray from object, work with it some time and than - boom!!! - your NSArray have different elements of number of it? What the hell? In that case, it's better idea to copy your inner implementation used NSMutableArray in getter.

Another example is some model object

@interface Person : NSObject <NSCopying>
@property NSString *name;
@property NSDate   *birthdate;
@end

And you have some other interface with property

@property (strong, readonly) Person *person;

Yeah, you will not assign different object to this property. However, you'll be able to modify its fields, so it will represent some completely different Person. If you don't want such behaviour - make it copy property. Or make it private with access methods to get its fields

- (id) getHiddenPersonPropertyValueForKey:(NSString *)personPropertyKey;

Or any other way

0
votes

If property represents really immutable value (NSArray, NSIndexSet, etc), then just readonly is fine, as it will be returned immutable as-is.

But in case of your private ivar being mutable (NSMutableArray ivar vs property's NSArray type), you should return a copy to prevent leaks of future internal changes into caller's state.

@interface MyObject : NSObject {
    NSMutableArray *_array;
}

@property(nonatomic, readonly) NSArray *array;
// -or-
- (NSArray *)array;

@end

and

@implementation

@dynamic array; // only if @property was declared in interface

- (NSArray *)array
{
    return [_array copy];
}

@end

The caller is safe then to store property's value and expect that it will not change even without making explicit copy itself:

self.array = [myObject array]; // e.g. 1 element
[myObject addElementToArray:@(42)];
NSLog(@"%@", self.array); // still 1 element