Objective-C 用于创建可变副本的模式
Objective-C pattern for creating mutable copies
我有许多 "model" 对象,其属性定义为 "readonly" 并在各个组件之间共享。
在某些情况下,我需要创建对象的本地可变副本(将它们用于本地可变状态)
我宁愿不实现 NSMutableCopy 协议,因为对象在创建后应该是不可变的。复制+变异操作后修改后的对象可能"passed"。
是否有建议的机制,或者我应该只实现接收 "changed" 参数的构造函数?
例如,一个将 JSON 解析为原生类型的对象:
@interface ImmutableObject : NSObject
// various "readonly" properties
...
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
@property (nonatomic, readonly) MyClass1 *prop1;
@property (nonatomic, readonly) MyClass2 *prop2;
...
@property (nonatomic, readonly) NSArray<MyClass100 *> *prop100;
@end
@implementation
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
self = [super init];
[self parseDictionaryToNative:jsonDictionary];
return self;
}
@end
代码中某处:
ImmutableObject *mutated = [immutableObject mutableCopy]; // best way to accomplish this?
// change some values...
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // save the new object
请注意 NSMutableArray
、NSMutableData
等 class 与它们的不可变对象不同。因此,在这种情况下,您可能应该定义一个 MutableObject
class 与 ImmutableObject
class 具有相同的接口(但具有可变属性)并在需要时使用它可变对象。
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // creates an ImmutableObject
ImmutableObject's
mutableCopy
的实现可能是这样的:
- (MutableObject *) mutableCopy
{
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
和 MutableObject's
copy
方法可能如下所示:
- (ImmutableObject *) copy
{
ImmutableObject *instance = [ImmutableObject new];
instance.prop1 = [self.prop1 copy];
// etc...
return instance;
}
您不会被迫正式使用 NSMutableCopy
协议,但您可以。
@spinalwrap 是正确的,但在这种情况下,没有理由在存储之前创建额外的副本。 NSMutableArray
是 NSArray
的子类,因此可以在任何可以使用 NSArray
的地方使用(这很常见)。你的也一样。在您的特定情况下,您可能会这样做:
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = mutated; // Since `state` is an immutable type,
// attempts to mutate this later will be compiler errors
这是安全的,因为您知道此代码块是唯一引用对象的可变版本的块(因为您在此处创建了它)。
就是说,一旦创建了可变子类,您现在需要考虑传递给您的任何 ImmutableObject
实际上可能是 MutableObject
的可能性,因此制作防御性副本(就像 NSArray
、NSString
等所做的一样)例如:
- (void)cacheObject:(ImmutableObject *)object {
// Need to copy here because object might really be a MutableObject
[self.cache addObject:[object copy]];
}
通过在 ImmutableObject
和 return self
上实现 copy
,并在 MutableObject
上实现 copy
作为实际副本,这变得相当有效,通常像这个:
ImmutableObject.m
- (ImmutableObject *)copy {
return self;
}
MutableObject.m
// as in spinalwrap's example
- (MutableObject *)mutableCopy {
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
// No need to duplicate code here. Just declare it immutable;
// no one else has a pointer to it
- (ImmutableObject *)copy {
return (ImmutableObject *)[self mutableCopy];
}
因此,如果对象已经是不可变的,则副本几乎是免费的。我说 "fairly efficient" 是因为它仍然会导致一些不必要的可变对象副本,这些副本永远不会发生变化。 Swift 的值类型的写时复制系统是专门为处理 ObjC 中的这个问题而创建的。不过以上是ObjC中的常见模式。
我有许多 "model" 对象,其属性定义为 "readonly" 并在各个组件之间共享。
在某些情况下,我需要创建对象的本地可变副本(将它们用于本地可变状态)
我宁愿不实现 NSMutableCopy 协议,因为对象在创建后应该是不可变的。复制+变异操作后修改后的对象可能"passed"。
是否有建议的机制,或者我应该只实现接收 "changed" 参数的构造函数?
例如,一个将 JSON 解析为原生类型的对象:
@interface ImmutableObject : NSObject
// various "readonly" properties
...
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary;
@property (nonatomic, readonly) MyClass1 *prop1;
@property (nonatomic, readonly) MyClass2 *prop2;
...
@property (nonatomic, readonly) NSArray<MyClass100 *> *prop100;
@end
@implementation
-(instancetype)initWithJSON:(NSDictionary *)jsonDictionary {
self = [super init];
[self parseDictionaryToNative:jsonDictionary];
return self;
}
@end
代码中某处:
ImmutableObject *mutated = [immutableObject mutableCopy]; // best way to accomplish this?
// change some values...
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // save the new object
请注意 NSMutableArray
、NSMutableData
等 class 与它们的不可变对象不同。因此,在这种情况下,您可能应该定义一个 MutableObject
class 与 ImmutableObject
class 具有相同的接口(但具有可变属性)并在需要时使用它可变对象。
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = [mutated copy]; // creates an ImmutableObject
ImmutableObject's
mutableCopy
的实现可能是这样的:
- (MutableObject *) mutableCopy
{
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
和 MutableObject's
copy
方法可能如下所示:
- (ImmutableObject *) copy
{
ImmutableObject *instance = [ImmutableObject new];
instance.prop1 = [self.prop1 copy];
// etc...
return instance;
}
您不会被迫正式使用 NSMutableCopy
协议,但您可以。
@spinalwrap 是正确的,但在这种情况下,没有理由在存储之前创建额外的副本。 NSMutableArray
是 NSArray
的子类,因此可以在任何可以使用 NSArray
的地方使用(这很常见)。你的也一样。在您的特定情况下,您可能会这样做:
MutableObject *mutated = [immutableObject mutableCopy]; // create an instance of MutableObject
mutated.prop1 = ... // change the value to something new
self.state = mutated; // Since `state` is an immutable type,
// attempts to mutate this later will be compiler errors
这是安全的,因为您知道此代码块是唯一引用对象的可变版本的块(因为您在此处创建了它)。
就是说,一旦创建了可变子类,您现在需要考虑传递给您的任何 ImmutableObject
实际上可能是 MutableObject
的可能性,因此制作防御性副本(就像 NSArray
、NSString
等所做的一样)例如:
- (void)cacheObject:(ImmutableObject *)object {
// Need to copy here because object might really be a MutableObject
[self.cache addObject:[object copy]];
}
通过在 ImmutableObject
和 return self
上实现 copy
,并在 MutableObject
上实现 copy
作为实际副本,这变得相当有效,通常像这个:
ImmutableObject.m
- (ImmutableObject *)copy {
return self;
}
MutableObject.m
// as in spinalwrap's example
- (MutableObject *)mutableCopy {
MutableObject *instance = [MutableObject new];
instance.prop1 = [self.prop1 copy]; // depends what you want here and what kind of class the properties are... do you need a deep copy? that might be a bit more work.
// etc...
return instance;
}
// No need to duplicate code here. Just declare it immutable;
// no one else has a pointer to it
- (ImmutableObject *)copy {
return (ImmutableObject *)[self mutableCopy];
}
因此,如果对象已经是不可变的,则副本几乎是免费的。我说 "fairly efficient" 是因为它仍然会导致一些不必要的可变对象副本,这些副本永远不会发生变化。 Swift 的值类型的写时复制系统是专门为处理 ObjC 中的这个问题而创建的。不过以上是ObjC中的常见模式。