How to implement mutableCopyWithZone and copyWithZone correctly

I read several other topics about it, but still I got lost.

I want to create 2 types of objects, one immutable only with read-only properties and one modified with only readwrite properties.

Lets call them EXCar and EXMutableCar.

EXCar is a subclass of NSObject, and EXMutableCar is a subclass of EXCar.

ExCar will have in its interface

@property (nonatomic, strong, readonly) NSString *name; 

EXMutableCar will have in its interface

 @property (nonatomic, strong) NSString *name; 

So, I “open” the EXCar properties when I use it with a subclass of EXMutableCar. And then it is volatile. The problem is the proper copying between them.

I implemented mutableCopyWithZone in EXCar:

 - (id)mutableCopyWithZone:(NSZone *)zone { EXMutableCar *mutableCopy = [[EXMutableCar allocWithZone:zone] init]; mutableCopy.name = _name; return mutableCopy; } 

First question, is this a good way to do this? (I want to get a copy of the swallow)

The problem is copyWithZone. Since the EXCar properties are readonly, I cannot create a new EXCar instance in EXCar or EXMutableCar and populate its properties as follows:

 - (id)copyWithZone:(NSZone *)zone { EXCar *copy = [[EXCar allocWithZone:zone] init]; copy.name = _name; // This can't work... return copy; } 

And I really don't want to do an “init” method with 15 properties to go through (of course, EXCar is an example, real classes are full of many properties). And usually they are initiated from a JSON message from the server, so they don’t need a complicated init method.

Second question: how to make copyWithZone that keeps my class unchanged?

Thank you for your help:)

+6
source share
1 answer

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 as “copying” protocols;
  • A subclass of EXMutableCar overrides the same property, making it readwrite .

The first thing in implementing EXMutableCar : manually @synthesize name , because Xcode gives us a warning, because we have the same property (but with a different access specifier) ​​in our superclass.

Please note that we could give the same name to our instance variable, for example _name , but it is important to understand that we declare a different variable in the subclass, since _name from the superclass is not available to us.

The Apple documentation further states:

If a subclass inherits NSCopying from its superclass and declares additional instance variables, the subclass must override copyWithZone: in order to properly process its own instance variables, first invoking the implementation of the superclass.

for NSMutableCopying:

If a subclass inherits NSMutableCopying from its superclass and declares additional instance variables, the subclass must override mutableCopyWithZone: in order to properly process its own instance variables, first referencing the implementation of the superclass.

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" 
+9
source

Source: https://habr.com/ru/post/951573/


All Articles