NSArray @property backed by a NSMutableArray

亡梦爱人 提交于 2019-12-09 07:35:05

问题


I've defined a class where I'd like a public property to appear as though it is backed by an NSArray. That is simple enough, but in my case the actual backing ivar is an NSMutableArray:

@interface Foo
{
    NSMutableArray* array;
}
@property (nonatomic, retain) NSArray* array;

@end

In my implementation file (*.m) I @synthesize the property but I immediately run into warnings because using self.words is the same as trying to modifying an NSArray.

What is the correct way to do this?

Thanks!


回答1:


I would declare a readonly NSArray in your header and override the getter for that array to return a copy of a private NSMutableArray declared in your implementation. Consider the following.

Foo.h

@interface Foo

@property (nonatomic, retain, readonly) NSArray *array;

@end

Foo.m

@interface Foo ()

@property (nonatomic, retain) NSMutableArray *mutableArray

@end

#pragma mark -

@implementation Foo

@synthesize mutableArray;

- (NSArray *)array
{
    return [[self.mutableArray copy] autorelease];
}

@end



回答2:


Basically, put the NSArray property in a category in your header file and the NSMutableArray property in the class extension in your implementation file. Like so...

Foo.h:

@interface Foo
@end

@interface Foo (Collections)
@property (nonatomic, readonly, strong) NSArray *someArray;
@end

Foo.m

@interface Foo ()
@property (nonatomic, readwrite, strong) NSMutableArray *someArray;
@end



回答3:


Simple:

1) Don't use a property when it ain't one.

2) Code simplifies to:

- (NSArray *)currentArray {
    return [NSArray arraywithArray:mutableArray]; // need the arrayWithArray -    otherwise the caller could be in for surprise when the supposedly unchanging array changes while he is using it. 
}

- (void)setArray:(NSArray *)array {
    [mutableArray setArray:array];
}

When the object is alloced create the array, when it dies, dealloc the array.

When large effects happen at the mere use of a '.' operator, its easy to overlook hugely inefficient code. Accessors are just that. Also - if someone calls aFoo.array - the contract is to get access to foo's array members - but really its just a copy at the time of the call. The difference is real enough that it caused bugs in the other implentations posted here.




回答4:


Update: this answer is not valid anymore. Use one of suggested solutions below.

These days you can do the following:

Foo.m:

@implementation Foo {
    NSMutableArray* _array;
}

@end

Foo.h:

@interface Foo

@property (readonly, strong) NSArray* array;

@end

You can still address mutable _array by ivar from the inside of implementation and outside it will be accessible via immutable property. Unfortunately this doesn't guarantee that others can't cast it to NSMutableArray and modify. For better protection from idiots you must define accessor method and return immutable copy, however that might be very expensive in some cases.

I would actually agree with one of the comments above that it's better to use simple accessor methods if you need to return some read-only data, it's definitely less ambiguous.




回答5:


That's because your property must match the actual ivar's class type.

A possible solution/workaround:

//Foo.h:
@interface Foo
{
    NSMutableArray* mutableArray;
}
@property (readwrite, nonatomic, retain) NSArray* array;
//or manual accessor declarations, in case you're picky about wrapper-properties.
@end

//Foo.m:
@interface Foo ()
@property (readwrite, nonatomic, retain) NSMutableArray* mutableArray;
@end

@implementation

@synthesize mutableArray;
@dynamic array;

- (NSArray *)array {
    return [NSArray arrayWithArray:self.mutableArray];
}

- (void)setArray:(NSArray *)array {
    self.mutableArray = [NSMutableArray arrayWithArray:array];
}

@end

You're adding a private mutableArray property in a class extension and making the public array simply forward to your private mutable one.

With the most recent language extensions of ObjC I tend to remove the

{
    NSMutableArray* mutableArray;
}

ivar block entirely, if possible.

And define the ivar thru the synthesization, as such:

@synthesize mutableArray = _mutableArray;

which will generate a NSMutableArray *_mutableArray; instance for you.




回答6:


Simplest answer: your property type (NSArray) doesn't match your instance variable type (NSMutableArray).

This is yet another good reason that you shouldn't define your own backing variables. Let @synthesize set up your instance variables; don't do it by hand.



来源:https://stackoverflow.com/questions/8407267/nsarray-property-backed-by-a-nsmutablearray

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!