The answer from dasblinkenlight is correct. The sample asked in the question is good. I provide an alternative that differs in two ways. Firstly, due to the unused iVar in a mutable class, the property is atomic. Secondly, as in many base classes, a copy of an immutable instance simply returns itself.
myobject.h:
@interface MyObject : NSObject <NSCopying, NSMutableCopying> @property (atomic, readonly, copy) NSString *value; - (instancetype)initWithValue:(NSString *)value NS_DESIGNATED_INITIALIZER; @end
Myobject.m
#import "MyObject.h" #import "MyMutableObject.h" @implementation MyObject - (instancetype)init { return [self initWithValue:nil]; } - (instancetype)initWithValue:(NSString *)value { self = [super init]; if (self) { _value = [value copy]; } return self; } - (id)copyWithZone:(NSZone *)zone { return self; } - (id)mutableCopyWithZone:(NSZone *)zone {
MyMutableObject.h:
#import "MyObject.h" @interface MyMutableObject : MyObject @property (atomic, copy) NSString *value; @end
MyMutableObject.m:
Test code snippet:
NSMutableString *string = [NSMutableString stringWithString:@"one"]; MyObject *object = [[MyObject alloc] initWithValue:string]; [string appendString:@" two"]; NSLog(@"object: %@", object.value); MyObject *other = [object copy]; NSAssert(object == other, @"These should be identical."); MyMutableObject *mutable1 = [object mutableCopy]; mutable1.value = string; [string appendString:@" three"]; NSLog(@"object: %@", object.value); NSLog(@"mutable: %@", mutable1.value);
Some debugging immediately after the last line above:
2017-12-15 21:51:20.800641-0500 MyApp[6855:2709614] object: one 2017-12-15 21:51:20.801423-0500 MyApp[6855:2709614] object: one 2017-12-15 21:51:20.801515-0500 MyApp[6855:2709614] mutable: one two (lldb) po mutable1->_value one two (lldb) po ((MyObject *)mutable1)->_value nil
As mentioned in the comments, the base class requires discipline to use getters instead of iVar. Many believe that this is good, but this discussion is not the topic here.
The slight difference that you might notice is that I used the copy attribute for the property. It can be made strong instead with very little code change.