Overwriting object ivars


#1

Playing around with BMITime and the Stocks projects, I noticed some strange behavior with the default @property and @synthesize accessors. Well, maybe it’s expected in C, but it kind of blows the water out of data encapsulation. As an example, if I have a class BMITime with an ivar object - say an Employee emp - then from main, I can write:

Employee *garbage = [myBMITime emp]; // I’ve stolen your reference, ie the reference to the actual emp
garbage = null; // and trashed it

So any random object in main can steal the reference to a totally separate object’s supposedly protected object ivar and do whatever it wants to it without using any of the setter methods specified.

Basically it seems like the @synthesize getter method returns the actual address of the actual ivar, instead of a copy of that ivar. Once that address is out there, any runaway Object - even one that’s not a parent or child - can overwrite the ivars of the class.

There’s got to be a way to specify to @synthesize that getters should return a copy of the ivar object (stored in separate memory) instead of the address to the actual ivar? That seems like a significant break with OO to have the default be the other way? Wouldn’t you be forced to define your own getters to provide a copy and not a pointer to the actual object to avoid this issue?

Thanks in advance for any help.


#2

First, let me take the superficial escape route and say that I don’t believe your code does what you think it does.

By assigning anything to garbage, you’re making it impossible for you to see the employee, but the employee is still out there; another garbage = [myBMITime emp] will get you a fresh copy of the pointer.

Having said that, yes, handing back a pointer to an important object is dangerous. There are a few types of danger I can think of:

[ul]
[li]The client (code that gets a pointer) can change attributes in the object without your knowledge that make you unhappy[/li]
[li]The client can call setEmp to give you an unexpected object (or null)[/li][/ul]

The second one can be addressed by some sanity checks in setEmp (or not providing such a method).

The first one could be protected against by not allowing direct access to emp, but by wrapping its methods in the intermediate class and only allowing changes you’re comfortable with.

If the client only needs read access to the object, you could clone the object (no language shortcut for that of which I’m aware) and hand back a copy.

You could also modify the original class (Employee in this case) to allow other objects to register as observers to changes, so if the client makes a change, your intermediate class is made aware of the change right away.


#3

I agree, the issue is the danger of passing pointers directly to important objects. The solution is to provide a pointer to a copy in the getter method. But @synthesize, by default, creates getter methods that are direct pointers - which seems like a bad idea for a default to me anyway.

Basically I’m too newb to figure out how to force @synthesize to create indirect - copying - getters, if that’s possible. Writing the getters by hand for objects I suppose is a decent enough workaround, but I’m always thinking of the lazy solution :slight_smile:


#4

I haven’t found a way to do so. There’s a copy attribute for @property, but that applies to setters, not getters, and only to objects which implement NSCopying.


#5

Yeah, somehow I suspect the answer lies in implementing the NSCopying protocol for important ivar objects, but for the moment I think that’s beyond me and a less useful solution than just manually creating accessors for all ivar objects.

I just wish @synthesize by default didn’t return direct pointers. Am I crazy in thinking that’s not exactly what most programs (should) expect when they ask for an object ivar?


#6

Not crazy, but I don’t think it’s a significant concern inside the same process space, with the same team of developers working on an application.

I think this topic becomes much more relevant and important when you’re talking about passing messages across a network, when you really don’t know who’s on the other side of the request, and in that case you’re serializing objects before sending them, so the remote client doesn’t have a way to directly impact your state without your awareness.


#7

I think I’ve sorted this topic out in my own mind. Hopefully this all sounds reasonable:

  1. It’s important to consider implementing the NSCopying protocol in data-sensitive classes you create.

  2. With NSCopying implemented, the synthesized setter method (with the copy @property) is fine, but the synthesized getter method still returns a direct pointer to your class objects, so consider overriding this by writing your own getter for those classes.

  3. For Foundation classes (like NSString or NSArray), even though they’re immutable pointers to them aren’t, so consider using the copy @property (the NSCopying protocol is already implemented on those classes). Again consider a custom getter that returns a copy instead of relying on that synthesized getter, which returns a direct pointer.

  4. With NSMutable (or other mutable) classes you wouldn’t ordinarly use a copy if the data is going to be directly accessed and changed.