Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Protect from adding object to NSMutableArray in public interface

I want to protect access to NSMutableArray in public interface

I am trying to do this by defining property as NSArray in public interface and as NSMutableArray in private interface like this:

@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end

@interface Order()
@property (readwrite, strong, nonatomic) NSMutableArray* comments;
@end

But this doesn't work - so I have to define property in public interface NSMutableArray instead:

@interface Order
@property (strong, nonatomic) NSMutableArray* comments;
@end

The goal is to provide read-only access to comments for API clients and full access to methods like addObject: in the implementation.

So defining goal more clearer:

  1. Client should have access to property as NSArray without ability to access mutation methods.
  2. Client should not have ability to update the comments to point to new value.
  3. The solution must be done without creating extra structures and array copying.

So simply the question was if it's possible to make public definition of property more general (NSArray instead of NSMutableArray).

Is there any other clean way to reach the goal or I have to use NSMutableArray everywhere?

RESOLUTION

After reviewing my original question and answers I realized that I would like to use more generic class NSArray in the public interface and NSMutableArray in the implementation - but it's just not possible for one property. So the answer is not possible.

So I will just use single property with NSMutableArray without any extra desired protection.

But I will also pick the most appropriate answer it might help if you really prefer protection over simplicity and efficiency.

like image 749
Vladimir Avatar asked Mar 31 '13 18:03

Vladimir


3 Answers

You don't need a public property if all you want is to allow clients to read the array.

Just create an accessor method that returns a copy of your private mutable array:

@interface Order : NSObject
- (NSArray *)allComments;
@end

@implementation Order ()
@property (nonatomic, strong) NSMutableArray * comments;
@end

@implementation Order

@synthesize comments;

- (NSArray *)allComments
{
    return [[self comments] copy];
}

@end

This pattern can be seen in e.g., NSView: constraints and subviews are internally mutable, but only exposed for reading via a single method returning a nonmutable array.

like image 60
jscs Avatar answered Dec 27 '22 04:12

jscs


One solution would be to declare the property as readonly as an NSArray. Then in your implementation, create a separate, writable property based on NSMutableArray.

In the .h:

@interface Order : NSObject
@property (readonly, strong, nonatomic) NSArray* comments;
@end

In the .m:

@interface Order()
@property (strong, nonatomic) NSMutableArray* internalComments;
@end

Instead of synthesizing the readonly property, write:

- (NSArray *)comments {
    return [self.internalComments copy];
}

In the .m you do everything with self.internalComments.

like image 40
rmaddy Avatar answered Dec 27 '22 06:12

rmaddy


A far cleaner solution, if it's acceptable for your use case, would be to declare the property as an NSArray, while backing it with an NSMutableArray. The client could technically modify the array by simply casting it to a mutable one, but you are making it clear that doing so would be a bad idea.

A @property is simply a wrapper around two methods, a getter and a setter, which are typically backed by a storage variable. The simplest way to implement this solution would be:

@interface Order : NSObject
{
    NSMutableArray *_comments;
}

@property (readonly, strong, nonatomic) NSArray *comments;

- (void)addComment:(Comment *)comment;

@end

@implementation Order
@synthesize comments=_comments; // Can be omitted if you use Xcode 4.4+

- (void)addComment:(Comment *)comment
{
    [_comments addObject:comment];
}

@end

If you want the user to be able to replace the entire array (order.comments = ...), remove the readonly attribute on the property and override the -setComments: method:

- (void)setComments:(NSArray *)array
{
    [_comments release]; // Can be omitted if you are using ARC
    _comments = [array mutableCopy];
}

It's worth noting that since Objective-C is a dynamic language, it's not possible to fully prevent someone from accessing a variable, as you can interface directly with the runtime or call methods by their selector if you really want to poke around in things you're not supposed to. All you can really do is make it clear that doing so is a bad idea.

like image 27
liclac Avatar answered Dec 27 '22 06:12

liclac