【问题标题】:Objective-C pattern for creating mutable copies用于创建可变副本的 Objective-C 模式
【发布时间】:2016-04-03 15:07:14
【问题描述】:

我有许多“模型”对象,它们的属性被定义为“只读”并在各个组件之间共享。

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

我宁愿不实现 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

【问题讨论】:

    标签: ios objective-c factory nscopying nsmutablecopying


    【解决方案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 的可能性,因此制作防御性副本(就像使用 @ 987654327@、NSString等)例如:

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

    通过在ImmutableObjectreturn 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];
    }
    

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

    【讨论】:

    • 由于类代表一个相当大的模型,创建 2 个单独的类非常繁琐且容易出错。我希望有一种类似“swift”的机制来定义“var”而不是“let”变量并自动获得可变版本。我求助于创建原始副本(从原始返回实际副本,而不是“自我”)并使用 KVC 更改“只读”属性,(因为我正在更改 ~2-3 道具)。
    【解决方案2】:

    请注意,NSMutableArrayNSMutableData 等与它们的不可变类是不同的类。因此,在这种情况下,您可能应该定义一个与ImmutableObject 类具有相同接口的MutableObject 类(但具有可变属性),并在您想要一个可变对象时使用它。

    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 协议,但您可以。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多