1

我想知道实现可变与不可变数据结构的正确模式是什么。我理解这个概念以及它是如何工作的,但是如果使用底层的 Cocoa 数据结构,我应该如何实现呢?NSSet我的意思是,例如,如果我使用 a 。可以说我有以下内容:

// 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:如果适用,不返回不可变副本?所以我基本上将 a 分配NSSet给一个NSMutableSet属性,不是吗?

编辑:在深入研究这个问题时,我发现了围绕这个问题的更多问题。

  1. mySet应该copy代替strong.
  2. 我的copyWithZone:实现也不对。我在第一篇文章中没有提到它,但该实现与数据结构 ( MyDataStructure) 的不可变版本有关。正如我所读到的,不可变数据结构实际上并没有创建副本,它们只是返回自己。这就说得通了。
  3. 由于 2.,我需要copyWithZone:在可变版本 ( 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(之前未列出)使用copyproperty 属性时我真的需要信号吗?

感谢您耐心读到这里。最好的。

4

2 回答 2

1

苹果是一种方式

在 Apple 的所有库中,使用的模式是,可以通过创建类的可变版本-mutableCopy,或者(假设类被称为NSSomething),然后可以通过方法-initWithSomething:(NSSomething*)something或创建它+somethingWithSomething:(NSSomething*)somethingNSMutableSomething总是继承自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

通过使用这种模式,底层数据结构总是可变的,它的不可变版本只是一个不可变的副本(或者是吗??)。

不。为了减少内存占用,不可变对象不需要与可变对象相同的开销。

复制带区

只要确保你使用[self class]soMyMutableDataStructure可以继承这个方法并返回它自己的类型,也不要忘记对-initafter的调用+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(注意没有所有权属性 - 因为它只是一个别名 - 也就是我们只需要它来合成 setter 和 getter)

@property (nonatomic) NSSet *mySet;

我的班级.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,以进一步保护 getter 和 setter。在您的原始代码中,您将@property (...) myMutableSet接口扩展名放入 .m 文件中。这会自动合成 getter 和 setter,因此即使声明显然是“私有的”,也可以调用[myDataStructure performSelector:@selector(setMutableSet:) withObject:someMutableSet];它并且尽管有“未声明的选择器”编译器警告,它仍然可以工作。

此外,在您的原始实现中-mySet

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

这将返回指向 的指针的副本_myMutableSet,类型转换为NSSet*. 因此,如果有人重新铸造它,因为NSMutableSet*他们可以改变底层的可变集。

于 2015-06-27T18:38:15.337 回答
0
  1. 我无法判断您的模式是否正确,这取决于.. 如果您更容易理解这种方法。那么它是正确的,但不要忘记为未来的开发人员添加评论。

  2. mySet是不可变的,因为您将其初始化为不可变的 NSSet..

  3. copy,关于那只是参考这个讨论

希望这对您有所帮助。

于 2015-06-27T04:07:23.690 回答