ref: http://stackoverflow.com/questions/4089238/implementing-nscopying
To implement NSCopying, your object must respond to the -copyWithZone: selector. Here’s how you declare that you conform to it:
1 |
@interface MyObject : NSObject <NSCopying> { |
Then, in your object’s implementation (your .m file):
1 2 3 4 |
- (id)copyWithZone:(NSZone *)zone { // Copying code here. } |
What should your code do? First, create a new instance of the object—you can call [[[self class] alloc] init] to get an initialized obejct of the current class, which works well for subclassing. Then, for any instance variables that are a subclass of NSObject that supports copying, you can call [thatObject copyWithZone:zone] for the new object. For primitive types (int, char, BOOL and friends) just set the variables to be equal. So, for your obejct Vendor, it’d look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
- (id)copyWithZone:(NSZone *)zone { id copy = [[[self class] alloc] init]; if (copy) { // Copy NSObject subclasses [copy setVendorID:[[self.vendorID copyWithZone:zone] autorelease]]; [copy setAvailableCars:[[self.availableCars copyWithZone:zone] autorelease]]; // Set primitives [copy setAtAirport:self.atAirport]; } return copy; } |
NSCopying in Base and Sub hierarchy
http://stackoverflow.com/questions/4472904/implementing-nscopying-in-subclass-of-subclass?rq=1
http://stackoverflow.com/questions/18673296/overriding-readonly-property-in-subclass
Base Class
The base is class is straightforward. We only have two member variables, but do not want others to access them. So we declare readonly in @property in the .h file. However, we DO want to modify them in our own implementation. We declare readwrite in our @property in the .m file:
Today.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@interface Today : NSObject <NSCopying> { @protected NSNumber * dayPerformance_PlacingCount; NSNumber * dayPerformance_PlacingAmount; } //how will it be accessed outside @property(atomic, readonly) NSNumber * dayPerformance_PlacingCount; @property(atomic, readonly) NSNumber * dayPerformance_PlacingAmount; -(id)init; -(id)initWithCount:(NSNumber*)count andAmount:(NSNumber*)amount; -(float) getAverage ; @end |
Notice the @protected above the member variables. I did this so that children classes can access these member variables.
Today.m
1 2 3 4 5 6 |
@interface Today() @property(atomic, retain, readwrite) NSNumber * dayPerformance_PlacingCount; @property(atomic, retain, readwrite) NSNumber * dayPerformance_PlacingAmount; @end |
Be sure to synthesize them. Its basically getter/setter = iVar
1 2 3 4 5 6 7 |
@implementation Today // getter,setter = iVar @synthesize dayPerformance_PlacingCount; // getter,setter = iVar @synthesize dayPerformance_PlacingAmount; |
In our case, since we we did not specify an iVar like this:
1 |
@synthesis variableName = _variableName |
dayPerformance_PlacingCount is the iVar
and
1 |
self.dayPerformance_PlacingCount |
makes it run through the setter/getter methods.
So the idea behind copyWithZone is that we need to make a deep copy. Hence everything should have its own address. Staying true to that assertion, we must first dynamically allocate the object to be returned:
1 |
id copy = [[[self class] alloc] init]; |
We create an empty self class. But wait, What about its member variables? They need to be unique with their own address. So we dynamically create those member variables like so:
1 2 |
NSNumber * tempCount = [NSNumber numberWithFloat:[self.dayPerformance_PlacingCount floatValue]]; NSNumber * tempAmount = [NSNumber numberWithFloat:[self.dayPerformance_PlacingAmount floatValue]]; |
Then we have our newly created object’s member variables retain them. the variable copy is our newly created Day variable:
1 2 |
[copy setDayPerformance_PlacingAmount:tempAmount]; [copy setDayPerformance_PlacingCount:tempCount]; |
..and then we just return that copy variable.
thus, it gets returned like so:
1 2 3 4 |
Day * myDay = [Day alloc] init]; Day * yourDay = [myDay copy]; ... ... |
Thus, yourDay takes on a whole newly allocated Day instance with its own member variables and all.
Base class’s copy method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//DEEP COPY: everyone has their own address - (id)copyWithZone:(NSZone *)zone { DLOG(@"Today.m - copyWithZone"); // Copying code here. DLOG(@"Deep Copy, alloc/init Base class"); Day * copy = [[[self class] alloc] init]; if (copy) { DLOG(@"Today.m - copy is valid"); //first let's create our own objects with unique addresses //all auto released NSNumber * tempCount = [NSNumber numberWithFloat:[self.dayPerformance_PlacingCount floatValue]]; NSNumber * tempAmount = [NSNumber numberWithFloat:[self.dayPerformance_PlacingAmount floatValue]]; //you can also do this: //[[[NSNumber alloc] initWithFloat:[self.dayPerformance_PlacingAmount floatValue]] autorelease] DLOG(@"Today.m - deep copying instance varaibles"); //have our iVars retain on them [copy setDayPerformance_PlacingAmount:tempAmount]; [copy setDayPerformance_PlacingCount:tempCount]; DLOG(@"Today.m - In Today, placing amount is: %@", [[copy dayPerformance_PlacingAmount] stringValue]); DLOG(@"Today.m - In Today, placing count is: %@", [[copy dayPerformance_PlacingCount] stringValue]); } DLOG(@"returning alloc'ed Base class"); return copy; } |
To use it in your main, you go:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Today * dayOne = [[Today alloc] initWithCount:[NSNumber numberWithFloat:609.00] andAmount:[NSNumber numberWithFloat:1204.50]]; DLOG(@"~~~~~~~~dayOne~~~~~~~~~~~~~"); DLOG(@"dayOne's address: %p", dayOne); DLOG(@"dayPerformance_PlacingAmount address: %p",dayOne.dayPerformance_PlacingAmount); DLOG(@"dayPerformance_PlacingAmount value: %f",[dayOne.dayPerformance_PlacingAmount floatValue]); DLOG(@"~~~~~~~~~~~~~~~~~~~~~"); Today * dayTwo = [dayOne copy]; DLOG(@"~~~~~~~~dayTwo~~~~~~~~~~~~~"); DLOG(@"dayTwo's address: %p", dayTwo); DLOG(@"dayPerformance_PlacingAmount's address: %p",dayTwo.dayPerformance_PlacingAmount); DLOG(@"dayPerformance_PlacingAmount's value: %f",[dayTwo.dayPerformance_PlacingAmount floatValue]); DLOG(@"~~~~~~~~~~~~~~~~~~~~~"); |
Result:
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 58] – ~~~~~~~~dayOne~~~~~~~~~~~~~
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 59] – dayOne’s address: 0x16dc1f80
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 60] – dayPerformance_PlacingAmount address: 0x16dc1fa0
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 61] – dayPerformance_PlacingAmount value: 1204.500000
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 62] – ~~~~~~~~~~~~~~~~~~~~~
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 66] – ~~~~~~~~dayTwo~~~~~~~~~~~~~
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 67] – dayTwo’s address: 0x16dc22a0
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 68] – dayPerformance_PlacingAmount’s address: 0x16dc22e0
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 69] – dayPerformance_PlacingAmount’s value: 1204.500000
-[AppDelegate application:didFinishLaunchingWithOptions:] [Line 70] – ~~~~~~~~~~~~~~~~~~~~~
As you can see, the Day addresses are different from the copy. As well as the member variables. In this particular case, I only printed out instance variable dayPerformance_PlacingAmount.