我打算使用一个NSMutableDictionary
属性来存储游戏数据(例如分数、设置等)。
@property (nonatomic, copy) NSMutableDictionary *gameData;
在研究为什么没有属性的“可变复制”选项时,我发现了这个讨论,接受的答案指出:
正确的做法是不要让可变数组成为属性
那么在现代 Objective-C 中将可变集合作为属性处理的最佳方法是什么?
我打算使用一个NSMutableDictionary
属性来存储游戏数据(例如分数、设置等)。
@property (nonatomic, copy) NSMutableDictionary *gameData;
在研究为什么没有属性的“可变复制”选项时,我发现了这个讨论,接受的答案指出:
正确的做法是不要让可变数组成为属性
那么在现代 Objective-C 中将可变集合作为属性处理的最佳方法是什么?
对于一个类来说,为一个集合对象公开一个可变属性是一种非常少见的模式,因为它似乎破坏了封装。
尽管如此,在某些情况下它可能是有意义的:例如,您可以为某些内部管理的集合返回一个 KVO 代理(使用KVC'smutableArrayValueForKey:
)。
在这些情况下,是否将 getter 声明为属性或普通方法并不重要。但是,如果是属性,它不应该是copy
属性。
为了使这一点更清楚,这里有一个例子。我们有一个声明一个公共 getter 的类:
@interface Foo : NSObject
@property (strong, readonly) NSMutableArray *publicBars;
@end
该实现管理一个封装的私有可变数组,名为_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];
}
这是花哨的部分:我们通过返回一个 KVC 代理来实现公共属性,该代理反映和改变内部状态而不暴露内部 ivars。它仅使用上面定义的公共访问器来更改内部数组。
- (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
)。虽然不是一个集合对象,但这是一个可变对象,它将突变传递给它的宿主对象,即文本视图。
恕我直言,您可以很好地使用可变属性 - 因为 Objective-C 中的属性不仅用于封装,还用于内存管理(通过 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
编辑:
此外,由于可变集合通常不是线程安全的,因此请确保始终从同一个线程更改它们。
可变属性的问题是其他类可以在所有者不注意的情况下更改集合的内容。一个好的设计认为只有一个类负责一项特定的任务,在这种情况下存储游戏数据。
我要做的是使可变字典成为strong
私有属性,并以以下形式向外界提供访问器方法:
- (void)storeObject:(id)object forSetting:(NSString *)settingName;
- (id)objectForSetting:(NSString *)settingName;
如果您确实需要外部类才能将字典设置为一个整体,那么 - 在确保确实有必要之后 - 您仍然可以将其设置为非可变属性,该属性在设置时会创建来自非可变字典中项目的内部可变字典。但我认为你应该能够围绕这种必要性进行设计。