'Copy' and mutable string


#1

I’m new to OOP and there’s a key point I’m obviously missing here.

In the ‘copy’ section of the chapter, there’s an example that creates a mutable string “Ono” and passes it to myObj as the last name.

Then “Lennon” is appended to the “Ono” string.

The book states that ‘copy’ prevents the last name of myObj from being changed. I’m not sure I understand why the appendString line WOULD change last name if copy weren’t there.

We set last name to x, THEN changed x. Why would last name change after changing x (if it weren’t for ‘copy’)?

Is this because x is a pointer?


#2

Exactly.

It’s important to remember that one of the primary features of an object-oriented program is that no object actually contains any other object. Let’s break it down, first by looking at what could happen, and then by seeing what we can do about it.

Let’s say I have a class, InsuranceReport. An InsuranceReport will hold a list of my most valuable possessions so that I can get reimbursed properly if my home gets blown away by a tornado. Here’s what you might see in InsuranceReport.h:

[code]@interface InsuranceReport : NSObject {
NSArray *valuableItems; // An array of my most precious possessions
}

@property (strong, nonatomic) NSArray *valuableItems;[/code]

Now consider a method in a mythical Person.m where I do some management of my possessions.

[code]
InsuranceReport *report = [[InsuranceReport alloc] init]; // Create the InsuranceReport instance to use someday.

// Lalala, time to start cataloguing my possessions so I know about everything I own.
// I’ll catalogue the most valuable ones first and add them to the report,
// then go back and finish cataloguing the rest.
[self setPossessions:[[NSMutableArray alloc] init]]; // First, I need a mutable array to keep track of them.
[[self possessions] addObject:@“Gold coins”];
[[self possessions] addObject:@“Rubies”];
[[self possessions] addObject:@“Diamonds”];

// I’ll add these items to the report and set it aside. Maybe I’ll send it off later.
[report setValuableItems:[self possessions]];

// Alrighty, time to get back to cataloguing.
[[self possessions] addObject:@“Wallet”];
[[self possessions] addObject:@“Book”];
[[self possessions] addObject:@“Towel”];[/code]

Now, if I were to inspect my possessions list and the list of valuable possessions in the insurance report, I’d find that they’re identical. Not just identical, my possessions instance variable and the report’s valuableItems instance variable point to the exact same address in memory!

When I said to the report,

I was telling the report to point its valuableItems instance variable at the same place where my possessions instance variable was pointing. This means that from this point forward, the report and I were sharing the list (array) of items! When I added “Wallet”, “Book”, and “Towel” to the array, both myself and the insurance report could see the changes to our shared list.

Here’s what our object ecosystem looked like in memory:

How can I prevent this?

The solution is to make sure that when I tell the report about my valuable possessions, the report makes a copy of my possessions array at that moment, so that when I add more items to my possessions list later, the report’s list is distinct and remains unchanged.

To do this, all I have to do is change the property declaration for the InsuranceReport class’ valuableItems instance variable:

By changing the strong keyword to copy, I’ve ensured that when someone sets a new valuableItems array on an InsuranceReport, the report will make a new copy of the array rather than merely maintaining a strong reference to it.

With that one simple change, my object ecosystem looks different (and much more like what I wanted) at the end of my cataloguing project:


#3

@MickeyWard,

Great explanation of that logic. This is often missed when reading the documentation and tutorials. Thanks!!


#4

I too was confused, and Mikey’s explanation just made it clear.

So in our Ono-Lennon case, if I code this in main.c :

[code]NSMutableString *x = [[NSMutableString alloc]initWithString:@“Ono”];
[myObj setLastName:x];

    NSLog(@"1. x is %@, lastName of myObj is %@", x, [myObj lastName]);
    
    [x appendString:@" Lennon"];
    NSLog(@"2. x is %@, lastName of myObj is %@", x, [myObj lastName]);

[/code]

I’ll get :

  1. x is Ono, lastName of myObj is Ono
  2. x is Ono Lennon, lastName of myObj is Ono

Thanks a lot