Reputation: 2973
I'm try to grok properties declared as both copy and readonly in objective-c, and specifically, whether I have to do the copy myself. In my init methods. Evidence suggests I do:
@interface A : NSObject
@property(nonatomic, copy, readonly) NSData *test;
- (instancetype)initWithData:(NSData *)data;
@end
@implementation A
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
_test = data;
}
return self;
}
@end
int main (void) {
NSData *d1 = [NSMutableData dataWithBytes:"1234" length:5];
A *a = [[A alloc] initWithData:d1];
NSLog(@"%lx", (unsigned long)d1);
NSLog(@"%lx", (unsigned long)a.test);
return 0;
}
I had thought I could do self.test = data
in my init method, but that is not permitted because it's readonly (not unexpectedly). Of course, self.test = [data copy]
ensures two different objects.
So: Is there a way to create a readonly property in objective-c that copies the incoming value, or is it sufficiently an edge case that the combination is pointless and I have to do any copying myself manually anyway?
Upvotes: 0
Views: 492
Reputation: 90551
In addition to what Darren said, the copy
attribute describes what semantics the properties setter has. In your initializer, you're not using the setter, you're directly assigning to the instance variable.
It's maybe a bit hard to grok, but the instance variable is not the same thing as the property. It is used to implement the property in this case. But, assigning to the instance variable is not the same as setting the property.
If you want your initializer to also have the semantics that it copies the passed-in data, that's a separate design decision (although a good idea to go with the property's semantics). You could implement that by using a private setter as Darren suggests, but you could also just do:
_test = [data copy];
in the initializer.
Upvotes: 0
Reputation: 25619
A @property
declaration is merely shorthand for some accessor/mutator method declarations, and (in some cases) synthesized implementations for said accessor/mutator methods.
In your case, the @property(nonatomic, copy, readonly) NSData *test
declaration expands to this equivalent code:
@interface A : NSObject
{
NSData* _test;
}
- (NSData*)test;
@end
@implementation A
- (NSData*)test
{
return _test;
}
@end
There is no setTest:
mutator method because the property is declared as readonly
, so the copy
attribute has no effect.
You can implement your own mutator method:
- (void)setTest:(NSData*)newValue
{
_test = [newValue copy];
}
Or, you can have the compiler synthesize a mutator method for you by declaring a read/write property in a private class extension in your implementation file:
// A.m:
@interface A()
@property (nonatomic, copy) NSData* test;
@end
Both cases would allow you to use the test
mutator method to copy a value to the _test
instance variable:
- (instancetype)initWithData:(NSData *)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
The end result is:
@interface A : NSObject
@property(nonatomic, copy, readonly) NSData* test;
- (instancetype)initWithData:(NSData*)data;
@end
@interface A()
@property (nonatomic, copy) NSData* test;
@end
@implementation A
- (instancetype)initWithData:(NSData*)data {
if ((self = [super init]) != nil) {
self.test = data;
}
return self;
}
@end
Upvotes: 1