2015-06-26 4 views
1

Мне интересно, какой будет правильный шаблон для реализации Mutable vs Immutable структур данных. Я понимаю концепцию и то, как она работает, но как я могу реализовать, используя базовую структуру данных Cocoa? Я имею в виду, например, если я использую NSSet. Допустим, у меня есть следующие:Правильный шаблон для mutable vs immutable

// MyDataStructure.h 
@interface MyDataStructure : NSObject 
@property (nonatomic, strong, readonly) NSSet * mySet; 
@end 


// MyDataStructure.m 
@interface MyDataStructure() 
@property (nonatomic, strong) NSMutableSet * myMutableSet; 
@end 

@implementation MyDataStructure 

- (NSSet *)mySet 
{ 
    return [_myMutableSet copy]; 
} 

@end 

Единственная причина, я использую изменяемый набор в качестве базовой структуры данных, так что изменяемые версия этого класса может искажать с ним. MyDataStructure per se действительно не нуждается в изменяемом наборе. Поэтому, если предположить, что я реализовал несколько инициализаторов, чтобы сделать этот класс полезным, вот как MyMutableDataStructure выглядит следующим образом:

// MyDataStructure.h (same .h as before) 
@interface MyMutableDataStructure : MyDataStructure 

- (void)addObject:(id)object; 

@end 

// MyDataStructure.m (same .m as before) 
@implementation MyMutableDataStructure 

- (void)addObject:(id)object 
{ 
    [self.myMutableSet addObject:object]; 
} 

@end 

Используя эту модель базовой структура данных всегда изменчива, и его неизменная версией является лишь неизменной копией (или это??).

Это также вызывает другой вопрос, возникающий при реализации протокола NSCopying. Вот пример реализации:

- (id)copyWithZone:(NSZone *)zone 
{ 
    MyDataStructure * copy = [MyDataStructure allocWithZone:zone]; 
    copy->_myMutableSet = [_myMutableSet copyWithZone:zone]; 

    return copy; 
} 

Не copyWithZone: вернуть неизменяемую копию, если это применимо? Поэтому я в основном присваиваю NSSet вместо NSMutableSet, не так ли?

Редактировать: Во время погружения в проблему я обнаружил еще несколько проблем, связанных с этой проблемой.

  1. mySet должен быть copy вместо strong.
  2. Неправильная реализация copyWithZone:. Я не упоминал об этом в первом сообщении, но эта реализация относится к неизменяемой версии структуры данных (MyDataStructure). Как я уже читал, неизменяемые структуры данных фактически не создают копию, они просто возвращаются. В этом есть смысл.
  3. Из-за 2. мне нужно было переопределить copyWithZone: в версии Mutable (MyMutableDataStructure).

Чтобы сделать вещи ясно:

// MyDataStructure.h 
@property (nonatomic, copy, readonly) NSSet * mySet; 

И

// MyDataStructure.m 
@implementation MyDataStructure 

- (id)copyWithZone:(NSZone *)zone 
{ 
    // We don't really need a copy, it's Immutable 
    return self; 
} 

- (id)mutableCopyWithZone:(NSZone *)zone 
{ 
    // I also implement -mutableCopyWithZone:, in which case an actual (mutable) copy is returned 
    MyDataStructure * copy = [MyMutableDataStructure allocWithZone:zone]; 
    copy-> _myMutableSet = [_myMutableSet mutableCopyWithZone:zone]; 

    return copy; 
} 

@end 

@implementation MyMutableDataStructure 

- (id)copyWithZone:(NSZone *)zone 
{ 
    return [self mutableCopyWithZone:zone]; 
} 

@end 

кажется сложным на первый, но я думаю, что я получаю повесить его. Таким образом, оставшиеся вопросы:

  1. Правильно ли шаблон?
  2. Получает ли getter для mySet измененный или неизменяемый экземпляр?
  3. (не указано выше) Нужен ли мне сигнал copy при использовании атрибута свойства copy?

Я ценю ваше терпение, чтобы прочесть это далеко. Лучший.

ответ

1

Apple, путь

Все через библиотеку Apple, шаблон, который используется, изменяемая версия класса может быть создана через -mutableCopy, или (скажем, класс называется NSSomething), то могут быть созданы с помощью методов -initWithSomething:(NSSomething*)something или +somethingWithSomething:(NSSomething*)something. NSMutableSomething всегда наследует от NSSomething, поэтому методы-конструкторы одинаковы. (То есть, +[NSArray arrayWithArray:] и +[NSMutableArray arrayWithArray:] возвращают свои соответствующие типы экземпляра, также вы передаете изменяемый объект, чтобы сделать неизменяемую копию, иначе [NSArray arrayWithArray:someNSMutableArrayObject])

Так вот как я бы это сделать:

Интерфейс

MyDataStructure.h

// MyDataStructure.h 
@interface MyDataStructure : NSObject 
@property (nonatomic, strong) NSSet * mySet; 
+ (instancetype)dataStructureWithDataStructure:(MyDataStructure*)dataStructure; 
- (instancetype)initWithDataStructure:(MyDataStructure*)dataStructure; 
@end 

MyMutableDataStructure.h

// MyMutableDataStructure.h 
#import "MyDataStructure.h" 
@interface MyMutableDataStructure : MyDataStructure 
@property (nonatomic, strong) NSMutableSet * mySet; // Only needs to redefine this property. The instantiation methods will be borrowed from the immutable class. 
@end; 

Реализация

MyDataStructure.m

@implementation MyDataStructure 

+ (instancetype)dataStructureWithDataStructure:(MyDataStructure *)dataStructure { 
    return [[self alloc] initWithDataStructure:dataStructure]; 
} 

- (instancetype)initWithDataStructure:(MyDataStructure *)dataStructure { 
    self = [super init]; 
    if (self) { 
     self.mySet = [NSSet setWithSet:dataStructure.mySet]; 
    } 
    return self; 
} 

- (instancetype)mutableCopy { 
    return [MyMutableDataStructure dataStructureWithDataStructure:self]; 
}  

@end 

MyMutableDataStructure.m

@implementation MyMutableDataStructure 

- (instancetype)initWithDataStructure:(MyDataStructure *)dataStructure { 
    self = [super init]; 
    if (self) { 
     self.mySet = [NSMutableSet setWithSet:dataStructure.mySet]; 
    } 
    return self; 
} 

@end 

С помощью этого шаблона базовой структуры данных всегда изменчива, и его неизменная версия является просто неизменной копией (или i сидеть??).

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

copyWithZone

Просто убедитесь, что вы используете [self class] так MyMutableDataStructure могут наследовать этот метод и вернуть свой тип, а также не забывать призыв к -init после +allocWithZone:

__typeof(self) просто объявляет переменную «copy», как и любой тип self, поэтому он полностью наследуется изменяемым подклассом.

- (id)copyWithZone:(NSZone *)zone 
{ 
    __typeof(self) copy = [[[self class] allocWithZone:zone] init]; // don't forget init! 
    copy.mySet = [self.mySet copyWithZone:zone]; 
    return copy; 
} 

^Этот метод входит в реализации MyDataStructure

В исходной реализации

// Мы на самом деле не нужна копия, это Неизменное

Хотя это может быть правдой для вашего проекта, это злоупотребление именованием. Метод, начинающийся с -copy, должен вернуть копию объекта.

Идя немного не по теме:

я хочу коснуться некоторых вещей, которые я видел в вашем оригинальный вопрос ... Первое о скрывается «изменяемый объект» с «неизменяемым объект» указатель ссылки , Возможно, вам тоже нужна эта функциональность, поэтому здесь более надежный способ сделать это и почему.

MyClass.h (не заметить не собственности атрибутов - потому что это только на самом деле псевдоним - иначе мы только это нужно для синтеза твоих сеттеров и добытчиков)

@property (nonatomic) NSSet *mySet; 

MyClass.m

@interface MyClass() { 
    NSMutableSet *_myMutableSet; 
} 
@implementation MyClass 

// returns a copy of the internal mutable set as an NSSet 
- (NSSet*)mySet { 
    return [NSSet setWithSet:_myMutableSet]; 
} 

// setter saves the internal mutable set as a copy of whatever set you hand it 
- (NSSet*)setMySet:(NSSet*)mySet { 
    _myMutableSet = [NSMutableSet setWithSet:mySet]; 
} 

Я определил _myMutableSet как ivar, чтобы защитить геттеры и сеттеры дальше. В исходном коде вы помещаете @property (...) myMutableSet в расширение интерфейса в файле .m. Это автоматически синтезирует геттеры и сеттеры, поэтому даже если декларация, по-видимому, «закрыта», можно вызвать [myDataStructure performSelector:@selector(setMutableSet:) withObject:someMutableSet];, и она будет работать, несмотря на предупреждение компилятора «Undeclared selector».

Кроме того, в вашей первоначальной реализации -mySet:

- (NSSet *)mySet { 
    return [_myMutableSet copy]; 
} 

Это возвращает копию указателя на _myMutableSet, типа отлиты как NSSet*. Поэтому, если кто-то перечислил его как NSMutableSet*, они могли бы изменить базовый изменяемый набор.

+0

Определенно хороший ответ. Позвольте мне подробнее рассмотреть это :) –

+0

При переопределении '-copyWithZone:' вы вызываете инициализатор '-init', а затем копируете набор. Может ли это быть реализовано как '[[[self class] allocWithZone: zone] initWithDataStructure: self]'?Кажется совершенно другим, поскольку набор не выделяется в той же зоне, но имеет ли это значение? –

+0

Причина, по которой я говорю это, потому что я использовал 'NS_UNAVAILABLE', чтобы использовать метод' -init' ... –

0
  1. Я не могу сказать, правилен ли ваш шаблон, это зависит от того, будет ли этот подход более понятным для вас. то это правильно, но не забудьте добавить комментарии для будущих разработчиков.

  2. mySet неизменен, потому что вы инициализирован как NSSet, которая неизменна ..

  3. copy, о том, что просто сослаться на этот discussion

Надеется, что это помог вам.

+0

Почему вы говорите, что я инициализирую 'mySet' как' NSSet'? Я не вижу, как –

+0

О проблеме с копией, это обсуждение на самом деле не применяется. Я не спрашиваю о 'copy' (атрибут) и' readonly' (также атрибут), а скорее о 'copy' (атрибут) и' copy' (сигнал) при совместном использовании. Я все равно благодарю вас, хотя :) –

Смежные вопросы