可变集合作为属性的最佳实践

我打算使用NSMutableDictionary属性来存储游戏数据(如分数,设置等)。

 @property (nonatomic, copy) NSMutableDictionary *gameData; 

在研究为什么没有“mutablecopy”选项的属性,我发现这个讨论 ,接受的答案说:

正确的做法是不使可变数组成为一个属性

那么什么是现代Objective-C中处理可变集合的最好方法呢?

对于一个集合对象来说,暴露一个可变属性是相当less见的,因为它似乎破坏了封装。

尽pipe如此, 某些情况下它可能是有意义的:例如,您可以为一些内部pipe理的集合(使用KVC的mutableArrayValueForKey:返回一个KVO代理。

在这些情况下,是否将getter声明为属性或纯方法并不重要。 在属性的情况下,它不应该是一个copy属性,但。

为了使这个更清楚一点,这里是一个例子。 我们有一个声明一个公共getter的类:

 @interface Foo : NSObject @property (strong, readonly) NSMutableArray *publicBars; @end 

该实现pipe理一个名为_bars的封装的专用可变数组:

 @implementation Foo { NSMutableArray *_bars; } 

这个数组的元素由KVC对多个访问器公开:

 - (NSUInteger)countOfBars { return [_bars count]; } - (id)objectInBarsAtIndex:(NSUInteger)idx { return _bars[idx]; } - (void)insertObject:(id)object inBarsAtIndex:(NSUInteger)idx { if (_bars == nil) _bars = [NSMutableArray array]; [_bars insertObject:object atIndex:idx]; } - (void)removeObjectFromBarsAtIndex:(NSUInteger)idx { [_bars removeObjectAtIndex:idx]; } 

这里有一个奇特的部分:我们通过返回反映和改变内部状态而不暴露内部ivars 的KVC代理来实现公共财产。 它仅通过使用上面定义的公共访问器来更改内部数组。

 - (NSMutableArray *)publicBars { return [self mutableArrayValueForKey:@"bars"]; } @end 

我们可以使用代理来更改内部集合:

 Foo *foo = [[Foo alloc] init]; NSMutableArray *bars = foo.publicBars; [bars addObject:@1]; [bars addObject:@2]; [bars removeObjectAtIndex:0]; // Now the internal _bars array is @[ @2 ]. 

Cocoa中有这个和类似模式的实际例子。 例如,有-[NSTextView textStorage] ,返回内部后备存储(从NSMutableAttributedString派生的对象)。 虽然不是集合对象,但它是一个可变对象,它将突变传递给它的主对象,即文本视图。

可变属性的问题在于其他类可以在所有者没有注意到的情况下更改集合的内容。 一个好的devise看到只有一个类负责一个特定的任务,在这种情况下存储游戏数据。

我会做的是使可变的字典strong私有财产,并提供访问者的方法,以外的世界的forms:

 - (void)storeObject:(id)object forSetting:(NSString *)settingName; - (id)objectForSetting:(NSString *)settingName; 

如果你真的需要外部类来设置字典作为一个整体,那么在确定这是真正必要的之后,你仍然可以使它成为一个非可变属性,当它被设置时,创build一个新的实例内部可变字典从不可变字典中的项目。 但我认为你应该可以围绕这个必要性进行devise。

恕我直言,你可以很好地使用一个可变的属性 – 因为Objective-C中的属性不仅用于封装,还用于内存pipe理(由ARC)。

但是,您应该将此属性保留在您的实现文件中,并将其暴露给其他类的非易变,只读块。

 //your_header_file.h @interface YouClass : NSObject @property (readonly) NSDictionary *gameState; @end //your_implementation_file.m @interface YouClass () @property (nonatomic, strong) NSMutableDictionary *gameState; @end @implementation SomeClass //deal with mutable state here @end 

编辑

而且,由于可变集合通常不是线程安全的,所以确保你总是从同一个线程中改变它们。