Objective c 可变与不可变的正确模式
我想知道实现可变与不可变数据结构的正确模式是什么。我理解这个概念及其工作原理,但是如果使用底层的Cocoa数据结构,我应该如何实现呢?我的意思是,例如,如果我使用Objective c 可变与不可变的正确模式,objective-c,immutability,nscopying,mutability,Objective C,Immutability,Nscopying,Mutability,我想知道实现可变与不可变数据结构的正确模式是什么。我理解这个概念及其工作原理,但是如果使用底层的Cocoa数据结构,我应该如何实现呢?我的意思是,例如,如果我使用NSSet。假设我有以下几点: // MyDataStructure.h @interface MyDataStructure : NSObject @property (nonatomic, strong, readonly) NSSet * mySet; @end // MyDataStructure.m @interface
NSSet
。假设我有以下几点:
// 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本身并不真正需要可变集。因此,假设我已经实现了一些初始化器,使这个类变得有用,下面是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
属性,对吗
编辑:在深入研究这个问题的同时,我发现了更多围绕这个问题的问题
mySet
应该是copy
而不是strong
我的copyWithZone:
实现也不正确。我在第一篇文章中没有提到它,但该实现与数据结构的不可变版本相关(MyDataStructure
)。正如我所读到的,不可变的数据结构实际上并不创建副本,它们只是返回它们自己。这是有道理的
由于2,我需要重写可变版本(MyMutableDataStructure
)中的copyWithZone:
)
要说清楚:
// MyDataStructure.h
@property (nonatomic, copy, readonly) NSSet * mySet;
及
一开始看起来很棘手,但我想我已经掌握了窍门。因此,剩下的问题是:
模式正确吗
mySet
的getter是否返回可变或不可变的实例
(之前未列出)使用copy
属性时,我真的需要copy
信号吗
我很感激你耐心地读到这里。
最好的
我不知道你的式样是否正确,这要看情况而定。。如果你更能理解这种方法。那么它是正确的,但是不要忘记为未来的开发人员添加注释
mySet
是不可变的,因为您将其初始化为不可变的NSSet
复制
,关于这一点,请参考此
希望这对你有所帮助。苹果就是出路
在苹果的所有库中,使用的模式是,可以通过-mutableCopy
创建类的可变版本,或者(假设该类被称为NSSomething
),然后可以通过方法-initWithSomething:(NSSomething*)something
或+something with something:(NSSomething*)something
NSMutableSomething
始终继承自NSSomething
,因此构造函数方法是相同的。(即,+[NSArray arraywhitharray::
和+[nsmutablearraywhitharray:]
返回各自的实例类型,同时传入可变对象以生成不可变副本,也称为[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
#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;
MyMutableDataStructure.h
// MyDataStructure.h
@interface MyDataStructure : NSObject
@property (nonatomic, strong) NSSet * mySet;
+ (instancetype)dataStructureWithDataStructure:(MyDataStructure*)dataStructure;
- (instancetype)initWithDataStructure:(MyDataStructure*)dataStructure;
@end
// 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
@implementation MyMutableDataStructure
- (instancetype)initWithDataStructure:(MyDataStructure *)dataStructure {
self = [super init];
if (self) {
self.mySet = [NSMutableSet setWithSet:dataStructure.mySet];
}
return self;
}
@end
MyMutableDataStructure.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
@implementation MyMutableDataStructure
- (instancetype)initWithDataStructure:(MyDataStructure *)dataStructure {
self = [super init];
if (self) {
self.mySet = [NSMutableSet setWithSet:dataStructure.mySet];
}
return self;
}
@end
通过使用此模式,底层数据结构始终是可变的,其不可变版本只是一个不可变副本(或者是??)
不需要。为了减少内存占用,不可变对象不需要与可变对象相同的开销
copyWithZone
只需确保使用[self class]
,这样MyMutableDataStructure
就可以继承这个方法并返回它自己的类型,而且不要忘记在+allocWithZone:
之后调用-init
\uu 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
开头的方法应返回对象的副本
有点离题:
我想谈谈我在你最初的问题中看到的一些事情…第一个是关于用“不可变对象”指针引用隐藏“可变对象”。也许你也需要这个功能,所以这里有一个更健壮的方法来实现它,以及为什么
h(注意没有所有权属性-因为它实际上只是一个别名-也就是说,我们只需要在合成setter和getter时使用它)
我的班级
@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];
}
我将\u myMutableSet
定义为ivar,以进一步保护getter和setter。在原始代码中,您将@property(…)myMutableSet
放在.m文件的接口扩展名中。这会自动合成getter和setter,因此即使声明显然是“私有的”,也可以调用[myDataStructure performSelector:@selector(setMutableSet:)with Object:someMutableSet];
尽管出现“未声明的选择器”编译器警告,它仍能工作
另外,在-mySet
的原始实现中:
- (NSSet *)mySet {
return [_myMutableSet copy];
}
这将返回指向\u myMutableSet
的指针副本,键入casted作为NSSet*
。因此,如果有人将其重新转换为NSMutab