用于创建可变副本的Objective-C模式

时间:2016-04-03 15:07:15

标签: ios objective-c factory nscopying nsmutablecopying

我有很多“模型”对象,其属性被定义为“只读”并在各种组件之间共享。

在某些情况下,我需要创建对象的本地可变副本(将它们用于本地可变状态)

我宁愿不实现NSMutableCopy协议,因为对象在创建后应该是不可变的。修改后的对象可以在复制+变异操作后“传递”。

是否有建议的机制,或者我应该只实现接收“已更改”参数的构造函数?

例如,将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

2 个答案:

答案 0 :(得分:1)

请注意means <- apply(df1, 2, mean) sds <- apply(df1, 2, sd) df1Rescaled <- data.frame(t(t(t(t(df1) - means)) / sds)) NSMutableArray等与其不可变对应的类不同。因此,在这种情况下,您可能应该定义一个NSMutableData类,其接口与MutableObject类相同(但具有可变属性),如果您想拥有一个可变对象,请使用它。

ImmutableObject

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; } 协议,但你可以。

答案 1 :(得分:1)

@spinalwrap是正确的,但在这种情况下,没有理由在存储之前创建额外的副本。 NSMutableArrayNSArray的子类,因此可以在任何可以使用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的可能性,因此制作防御性副本(就像已完成NSArrayNSString等。例如:

- (void)cacheObject:(ImmutableObject *)object {
    // Need to copy here because object might really be a MutableObject
    [self.cache addObject:[object copy]];
}

通过在copyImmutableObject上实施return self,并在copy上实施MutableObject作为实际副本,通过以下方式提高效率:

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];
}

因此,如果对象已经是不可变的,那么副本几乎是免费的。我说“相当有效”,因为它仍然会导致一些永不变异的可变对象的不必要副本。 Swift用于值类型的写时复制系统专门用于处理ObjC中的这个问题。但以上是ObjC中的常见模式。