3

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;

Community
  • 1
  • 1
Boon
  • 40,656
  • 60
  • 209
  • 315
  • http://stackoverflow.com/questions/10694676/what-is-the-correct-way-to-declare-a-readonly-property-for-ios-using-arc – hariszaman Jun 19 '15 at 13:32

3 Answers3

5

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.

Wain
  • 118,658
  • 15
  • 128
  • 151
0

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

Sergii Martynenko Jr
  • 1,407
  • 1
  • 9
  • 18
  • Why would it matter if it is mutable though? The copy behavior simply needs to be defined by whoever implements the setter right? – Boon Jun 19 '15 at 13:55
  • If you implement the setter yourself - its up to you what it actually returns (as long as it corresponds to return value type). But you can simply `@synthesize` it. So, for compiler to know that you need to copy actual implementation of this property – Sergii Martynenko Jr Jun 19 '15 at 13:58
  • What I mean is copy attribute should have no bearing on whether you are using mutable or immutable array, since it's readonly. Is that not the case? – Boon Jun 19 '15 at 14:58
  • Readonly means that you cannot set completely new object (set pointer to another object in memory). It doesn't mean that object itself will be immutable - just like in second example. – Sergii Martynenko Jr Jun 19 '15 at 15:03
  • I understand what you say, and, in fact I think a copy property should also copy in the **getter** for this reason. However, by convention, I think that an object that contracts to return an immutable object is breaking the contract if it then mutates the object behind the caller's back. – JeremyP Jun 19 '15 at 15:25
  • Because of in capsulation principle it doesn't matters what actually happens of your implementation. If you have property NSArray it doesn't mean that there's any actual NSArray stored in memory - it can be something you store on filesystem. – Sergii Martynenko Jr Jun 20 '15 at 10:32
0

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
user3125367
  • 2,920
  • 1
  • 17
  • 17