0

I'm fighting with something and I don't find any satisfying solution.

I have a class with a "myMutableArray" member.

I would like the class to manage itself adding and removing items from the array, so I don't want any other class being able to access the member and call NSMutableArray methods on it.

In an ideal situation, I would like to have a private getter (to be able to call self.myMutableArray) and a public setter for this member.

Do you know how I may achieve this ?

In other words :

I would like other classes

be able to call

- [oneInstance setMyMutableArray:thisArray]; // set
- oneInstance.myMutableArray = thisArray; // set using setter

- thisArray = oneInstance.myMutableArray; // get
- [oneInstance addItem:anItem]; // add

not being able to call :

- [oneInstance.myMutableArray add:etc...] // add

I would like my class

be able to call

- self.myMytableArray = [NSMutableArray array]; // set
- thisArray = self.myMytableArray ; // get

Thank you.

Oliver
  • 23,072
  • 33
  • 138
  • 230
  • 1
    Note that one item you said should be allowed, `[oneInstance setMyMutableArray:thisArray];`, and one item you said should NOT be allowed, `oneInstance.myMutableArray = thisArray;` are exactly the same thing. Dot notation DOES call setters; you're not accessing the array directly when you write `oneInstance.myMutableArray`. – andyvn22 Dec 30 '10 at 21:07

2 Answers2

1

Is there any reason you need the public setter? It sounds like the class itself owns the array. You'd probably be better off not providing any public property access to the field, and making a public method which copies the values into your private field.

// public interface, in the .h file
@interface MyClass : // superclass, protocols, etc.
- (void) setSomething:(NSArray *)values;
@end

// private interface, not in the .h
@interface MyClass ()
@property (/* attributes */) NSMutableArray *myMutableArray;
@end

@implementation MyClass
@synthesize myMutableArray = myMutableArray_;

- (void) setSomething:(NSArray *)values
{
    [self.myMutableArray setArray:values];
}

@end
codelark
  • 12,254
  • 1
  • 45
  • 49
  • Thank you. I'm looking at this solution. Doing this, how may I offer a getter for outside calls, but prevent calls like [oneInstance.myMutableArray add:etc...] ? – Oliver Dec 30 '10 at 14:34
  • You can't prevent anyone from sending messages to anything. If you want it to be safe, provide a method which returns a non-mutable copy of the array. – codelark Dec 30 '10 at 14:36
  • Great Idea. applyed with another solution I found, its shoul be nearly perfect. See at :http://stackoverflow.com/questions/3571539/declaring-private-member-variables – Oliver Dec 30 '10 at 14:43
  • Would callers be able to use the dot notation for the `something=`? – Dan Rosenstark Dec 30 '10 at 14:47
  • You can declare "something" as a property, use the @dynamic keyword to let the compiler know you will provide the methods yourself, and use the methods to present the non-mutable interface to your mutable array. – codelark Dec 30 '10 at 14:51
  • @dynamic is not needed when you are providing the methods yourself in the .m file. It is only needed when you are going to dynamically provide the implementations at runtime. – bbum Dec 30 '10 at 18:05
  • @dynamic isn't necessary, but it will force the compiler to warn if you do not supply methods matching the correct signatures. – codelark Dec 30 '10 at 20:48
0

Foo.h

@interface Foo : NSObject
@property(readonly, retain) NSArray * myReadonlyArray;
- (void) addItem: (Item *) anItem;
- (BOOL) publiclyDoSomething;
@end

Foo.m

@interface Foo()
@property(readwrite, retain) NSMutableArray * myMutableArray;
- (void) doSomethingInPrivate;
@end

@implementation Foo
@synthesize myMutableArray = myMutableArray_;

- (void) addItem: (Item *) anItem
{
    // assuming myMutableArray_ was already iniitialized
    [self.myMutableArray addObject: anItem];
}

- (NSArray *)myReadonlyArray
{
     return self.myMutableArray;
}

... rest of methods (including the public/private) implementations ...
@end

Some details:

  • Objective-C has "instance variables", not "member variables".

  • The above defines a public getter and private setter that is synthesized automatically. For clarity's sake, I also added a public method and a private method.

  • "Public" and "private" in Objective-C are defined entirely by visibility to the compiler. The setter for myMutableArray and the method doSomethingInPrivate are only private because their declarations in an @interface cannot be imported.

  • self.myMutableArray and [self myMutableArray] do the same thing; the . syntax is merely short hand for an equivalent method call (with a few edge case details beyond this question)

  • @property in the @interface is purely short hand for method declarations (with a bit of extra metadata).

  • @interface Foo() is a class extension and not a category. It exists for exactly the purpose demonstrated above; to extend the @interface of a class with additional declarative information whose scope should be limited. It can appear in a header file that, say, you only import in your library's implementation to create library-private functionality.

  • @dynamic is used when you neither @synthesize an @property nor provide a conventional method implementation. It is not needed otherwise!

I'm probably forgetting something.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • Perfect. Just 2 questions : Is it possible to make the public getter as "copy" and the private getter as retain ? I guess so. Why do you say the @interface Foo() is not a category ? It looks like one... Is this just syntax or are there something deep inside this difference you make ? – Oliver Dec 31 '10 at 02:12
  • 2
    This still lets external objects do `[[foo myMutableArray] addObject:baz]`... What about declaring the property as an `NSArray` and not an `NSMutableArray`? – Dave DeLong Dec 31 '10 at 02:59
  • Added readonly array retrieval w/immutable return.l No need to create an actual NSArray as the caller *cannot* differentiate between a mutable and immutable array intelligently anyway. – bbum Feb 19 '11 at 22:34