10
votes

I might be missing something obvious here, but I'm implementing NSCopying on one of my objects. That object has private instance variables that are not exposed via getters, as they shouldn't be used outside the object.

In my implementation of copyWithZone:, I need alloc/init the new instance, but also set up its state to match the current instance. I can obviously access current private state from inside copyWithZone:, but I can't set it into the new object, because there are no accessors for that state.

Is there a standard way around this while still keeping data privacy intact?

Thanks.

3

3 Answers

8
votes

First, you should always have getters, even if they're private. Your object should only access even its own ivars using accessors (except in a very small number of cases). This will save you a great deal of suffering over memory management.

Second, Alex's suggestion of using -> is a standard approach, even though this violates the getters rule above. There are a small number of exceptions to that rule, and copy is one of. Using private setters here is still reasonable (and I used to do it that way exclusively), but I've found for various reasons that using -> often works out cleaner.

Be very careful to get your memory management correct. If you need to call [super copyWithZone:], then you should also read up on the complexities of NSCopyObject() and how it impacts you even if you don't use it yourself. I've discussed this at length in "NSCopyObject() considered harmful."

5
votes

You can access the instance variables of the copy directly. You use the same pointer dereferencing syntax you would use with a struct. So, for example, if your class is this:

@interface MyCopyableClass : NSObject {
    int anInstanceVariable;
}
@end

You can do this:

- (id)copyWithZone:(NSZone *)zone {
    MyCopyableClass *theCopy = [[[self class] allocWithZone:zone] init];
    theCopy->anInstanceVariable = anInstanceVariable;
    return theCopy;
}
1
votes

One option is to create a custom initializer that accepts the private iVar values. So you create it like:

-(id) initWithPropertyOne:(SomeClass *) anObject andPropertyTwo:(SomeClass *) anotherObject;

When you instantiate the copy, just use the custom initializer.