Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Objective c 可变与不可变的正确模式_Objective C_Immutability_Nscopying_Mutability - Fatal编程技术网

Objective c 可变与不可变的正确模式

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

我想知道实现可变与不可变数据结构的正确模式是什么。我理解这个概念及其工作原理,但是如果使用底层的Cocoa数据结构,我应该如何实现呢?我的意思是,例如,如果我使用
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