Code:
// EXCar.h
#import <Foundation/Foundation.h>
@interface EXCar : NSObject <NSCopying, NSMutableCopying>
@property (nonatomic, strong, readonly) NSString* name;
@end
// EXCar.m
#import "EXCar.h"
#import "EXMutableCar.h"
@implementation EXCar
- (id)copyWithZone:(NSZone *)zone {
EXCar* car = [[[self class] allocWithZone:zone] init];
car->_name = [_name copyWithZone:zone];
return car;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
EXMutableCar* mutableCar = [[EXMutableCar allocWithZone:zone] init];
mutableCar.name = [_name mutableCopyWithZone:zone];
return mutableCar;
}
@end
// EXMutableCar.h
#import "EXCar.h"
@interface EXMutableCar : EXCar
@property (nonatomic, strong) NSString* name;
@end
// EXMutableCar.m
#import "EXMutableCar.h"
@implementation EXMutableCar
@synthesize name = _mutableName;
- (id)copyWithZone:(NSZone *)zone {
EXMutableCar* car = [super copyWithZone:zone];
car->_mutableName = [_mutableName copyWithZone:zone];
return car;
}
- (id)mutableCopyWithZone:(NSZone *)zone {
EXMutableCar* car = [super mutableCopyWithZone:zone];
car->_mutableName = [_mutableName mutableCopyWithZone:zone];
return car;
}
Explanation:
EXCar
interface implements both "copying" protocols;- The subclass
EXMutableCar
overrides the same property, making itreadwrite
.
First thing in EXMutableCar
implementation: manually @synthesize name
because Xcode gives us a warning, since we have the same property (but with different access specifier) in our superclass.
Note we could have given the same name to our instance variable, like _name
, but it is important to understand that we declare in subclass a different variable, since _name
from the superclass is inaccessible for us.
Next, Apple documentation states:
If a subclass inherits NSCopying from its superclass and declares additional instance variables, the subclass has to override
copyWithZone:
to properly handle its own instance variables, invoking the superclass’s implementation first.
same for NSMutableCopying:
If a subclass inherits NSMutableCopying from its superclass and declares additional instance variables, the subclass has to override
mutableCopyWithZone:
to properly handle its own instance variables, invoking the superclass’s implementation first.
We do declare additional instance variables, so we override these methods in our subclass as well.
Result:
EXCar* car = [[EXCar alloc]init]; // car.name is (null)
EXCar* carCopy = [car copy]; // we can do this
EXMutableCar* mutableCar = [car mutableCopy]; // and this
mutableCar.name = @"BMW";
car = [mutableCar copy]; // car.name is now @"BMW"
EXMutableCar* anotherMutableCar = [car mutableCopy]; //anotherMutableCar.name is @"BMW"