4

Coming from a Java background, I'm having trouble figuring out ways to program defensively in Objective-C.
Assuming SomeClass is mutable and provides a copy method, this is a typical piece of code I'd write in Java:

public MyClass  
{ 
    private SomeClass customerList;
    ...
    public SomeClass getCustomerList() {
        return this.customerList.copy(); 
    }
    public void setCustomerList(SomeClass list) {
        this.customerList = list.copy();
    }
}

I took me some time to figure out that

@property (nonatomic, copy) SomeClass *customerList;  

would make a copy of the setter's argument before assigning it to the customerList property. What confuses me is writing an appropriate getter. So far it looks like this:

(SomeClass *)customerList {  
    if(!_customerList) {  
        _customerList = [[SomeClass alloc] init];
    }
    return _customerList;
}  

which works for all internal method calls like self.customerList = ..., but would pass a direct pointer to any external call creating a security breach. I was considering providing a different public getter that would return a copy, but would like to avoid it as it would need to have an unconventional name. How would you go about this situation?
Thank you.

Dmitry
  • 161
  • 1
  • 1
  • 10

2 Answers2

3

You can override the -customerList implementation to be: return [_customerList copy];. Be aware that's not usually how others expect accessors to work so make sure to document this.

Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
  • Also note that it violates the memory guidelines. `customerList` isn't allowed to delegate ownership to the caller. – JustSid Jan 05 '13 at 18:57
  • 1
    @JustSid You are right I was too hasty! I'm not sure actually how this would work in ARC. In manual retain / release you'd have to add an `autorelease`. EDIT: thinking it over I think ARC will insert the autorelease for you, but I'm not totally sure... – Carl Veazey Jan 05 '13 at 18:58
  • @Carl Veazey From what I understand, accessors should not provide *direct* access to the internal implementation of my class. Returning a copy provides all the information the outside world might need, yet will not let it directly modify it. – Dmitry Jan 05 '13 at 19:06
  • @JustSid This is exactly what I'm trying to avoid. – Dmitry Jan 05 '13 at 19:08
  • @Dmitry Usually this is solved by only passing around immutable objects, unless the caller is explicitly allowed to modify the object. – JustSid Jan 05 '13 at 19:08
  • @Dmitry I'm not sure I follow you - I thought you wanted to return a copy so that no outsiders could mutate the property owned by the object but instead can only mutate copies of it? – Carl Veazey Jan 05 '13 at 19:10
  • @Carl Veazey In Java I would directly modify the _customerList ivar in any internal method, and would have the getter return a copy of the customerList object to the outside calls. Objective-C, however, encourages to use the dot notation when it comes to accessing properties, regardless where the call is coming from. Also, conventions dictate that the getter for the customerList property should have the same name as the property. As it stands, MyClassObject.customerList will pass a pointer which is undesirable. – Dmitry Jan 05 '13 at 19:20
  • @Carl Veazey That is exactly what I'm trying to do. Only I don't need it to return a copy to internal methods. Sorry if I'm not making myself clear enough. – Dmitry Jan 05 '13 at 19:23
  • @Dmitry OK, so the conflict is that you wish to use the same accessors internally and externally while prohibiting callers from accessing the actual instance stored in the property? I don't know of a way to do that unfortunately without using separate methods. Sorry. – Carl Veazey Jan 05 '13 at 19:27
  • @Carl Veazey Exactly! Looks like I'm going to have to write separate internal accessors. Thank you for trying to help:) – Dmitry Jan 05 '13 at 19:33
0

If you want to return a copy backed by a property and its getter, it's pretty easy to use this form:

@interface MyClass : NSObject
- (SomeClass *)copyCustomerList;
@end

@interface MyClass ()
@property (nonatomic, copy) SomeClass * customerList; // hide what you can
@end

@implementation MyClass
- (SomeClass *)copyCustomerList { return self.customerList.copy; }
@end

although you could implement your own getter instead -- it is unconventional in ObjC, as Carl mentions.

Another approach you could take is to use a different name for the actual property:

@interface MyClass : NSObject
- (SomeClass *)customerList;
@end

@interface MyClass ()
@property (nonatomic, copy) SomeClass * privCustomerList;
@end

@implementation MyClass

- (SomeClass *)customerList
{
 // -autorelease if MRC
 return self.privCustomerList.copy;
}

@end
justin
  • 104,054
  • 14
  • 179
  • 226