Шаблон Objective-C для создания изменчивых копий

У меня есть много "модельных" объектов, свойства которых определены как "только для чтения" и разделены между различными компонентами.

В некоторых случаях мне нужно создавать локальные изменяемые копии объектов (используя их для локального изменяемого состояния)

Я скорее не реализую протокол 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 ответа

Решение

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

Это сделано довольно эффективным путем реализации copy на ImmutableObject а также 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.

Обратите внимание, что 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'smutableCopy может быть что-то вроде:

- (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'scopy Метод может выглядеть так:

- (ImmutableObject *) copy
{
    ImmutableObject *instance = [ImmutableObject new];
    instance.prop1 = [self.prop1 copy];
    // etc...
    return instance;
}

Вы не обязаны использовать NSMutableCopy протокол официально, но вы можете.

Другие вопросы по тегам