Objective-C中的不可变对象:大的init方法? [英] Immutable Object in Objective-C: Big init method?

查看:50
本文介绍了Objective-C中的不可变对象:大的init方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Objective-C中有一个带有不可变字段的对象.

I want to have an Object with immutable fields in Objective-C.

在C#中,我将使用带有私有设置程序和大型构造函数的Properties.

In C#, I would use Properties with private setters and a big constructor.

我将在Objective-C中使用什么?

What would I use in Objective-C?

使用@property似乎不允许我将设置器声明为私有.

Using @property doesn't seem to allow me to declare the setter as private.

使用

initWithData: (NSString*) something createDate: (NSDate*) date userID: (long) uid

如果要设置的属性超过4个,似乎太冗长了.

seems overly verbose if I have more than 4 properties to set.

我会在.h文件中声明吸气剂,而仅在.m中声明setter吗?

Would I declare the getters in the .h file and the setters only in .m?

我需要在某些日期和日期上使用保留或复制(顺便说一句:我应该使用这两个中的哪一个?),所以我需要在setter中添加一些代码.

I need to use retain or copy on something and date (by the way: which of these two should I use?), so I need some code in the setter.

或者还有诸如不变关键字之类的东西吗?

Or is there even something else like an immutable keyword?

推荐答案

如果您确实需要一个公共只读属性,则可以使用私有读写属性为该类中的属性提供设置器.但是,您应该考虑是否有必要.

You can have a public read-only property, and use a private read-write property to provide a setter for the property within your class if you really need one. However, you should consider whether it's even necessary.

作为示例,请考虑以下关于不可变Person类的声明和定义:

As an example, consider the following declaration and definition of an immutable Person class:

// Person.h
#import <Foundation/Foundation.h>

@interface Person : NSObject {
@private
    NSString *name_;
    NSDate *dateOfBirth_;
}

@property (readonly, copy) NSString *name;
@property (readonly, copy) NSDate *dateOfBirth;

/*! Initializes a Person with copies of the given name and date of birth. */
- (id)initWithName:(NSString *)name dateOfBirth:(NSDate *)dateOfBirth;

@end

// Person.m
#import "Person.h"

@implementation Person

@synthesize name = name_;
@synthesize dateOfBirth = dateOfBirth_;

- (id)initWithName:(NSString *)name dateOfBirth:(NSDate *)dateOfBirth {
    self = [super init];
    if (self) {
        name_ = [name copy];
        dateOfBirth_ = [dateOfBirth copy];
    }

    return self;
}

- (void)dealloc {
    [name_ release];
    [dateOfBirth_ release];

    [super dealloc];
}

@end

首先,请注意,我没有声明了Person.m中的类扩展,该类扩展将namedateOfBirth属性重新声明为readwrite.这是因为该类的目的是不可变的.如果实例变量仅在初始化时设置,则不需要设置器.

First, notice that I did not declare a class extension in Person.m that redeclares the name and dateOfBirth properties as readwrite. This is because the purpose of the class is to be immutable; there's no need to have setters if the instance variables are only ever going to be set at initialization time.

还要注意,我用不同的名称声明了实例变量,而不是属性.这清楚地表明了属性是作为类的编程接口,而实例变量是类的实现细节.我已经看到太多的开发人员(尤其是Mac OS X和iOS的新开发人员,包括许多来自C#的开发人员)将属性与可用于实现它们的实例变量进行了合并.

Also notice that I declared the instance variables with different names than the properties. This makes clear the distinction between properties as a programmatic interface to the class, and instance variables as an implementation detail of the class. I've seen far too many developers (especially those new to Mac OS X and iOS, including many coming from C#) conflate properties with the instance variables that may be used to implement them.

要注意的第三件事是,我将这两个属性都声明为copy,即使它们是只读的也是如此.有两个原因.首先是虽然此类的直接实例是不可变的,但没有什么可以阻止创建MutablePerson子类.实际上,这甚至可能是可取的!因此copy明确指定了超类的期望-namedateOfBirth属性本身的值不会改变.这也暗示了-initWithName:dateOfBirth:也可能复制.它的文档注释应该清楚说明.其次,NSString和NSDate都是值类.不可变副本的副本应该便宜,而且您不想挂在一个可变子类的实例上,而该可变子类会从您自己的类中变出来. (现在实际上没有任何可变的NSDate子类,但这并不意味着某人无法创建自己的...)

A third thing to notice is that I declared both of these properties as copy even though they're read-only. There are two reasons. The first is that while direct instances of this class are immutable, there's nothing preventing the creation of a MutablePerson subclass. In fact, this might even be desirable! So the copy specifies clearly what the expectations of the superclass are - that the values of the name and dateOfBirth properties themselves won't change. It also hints that -initWithName:dateOfBirth: probably copies as well; its documentation comment should make that clear. Secondly, both NSString and NSDate are value classes; copies of immutable ones should be inexpensive, and you don't want to hang onto an instance of a mutable subclass that will change out from under your own class. (Now there's not actually any mutable subclass of NSDate, but that doesn't mean someone couldn't create their own...)

最后,不必担心您指定的初始化程序是否冗长.如果对象的实例除非处于某个特定状态才是无效的,则您指定的初始化程序需要将其置于该状态-并且它需要采用适当的参数来实现.

Finally, don't worry about whether your designated initializer is verbose. If an instance of your object is not valid unless it's in some particular state, then your designated initializer needs to put it in that state -- and it needs to take the appropriate parameters to do so.

还有另外一件事:如果要创建这样的不可变值类,则可能还应该实现自己的-isEqual:-hash方法以进行快速比较,并可能也符合NSCopying.例如:

There's one more thing: If you're creating an immutable value class like this, you should probably also implement your own -isEqual: and -hash methods for fast comparison, and probably conform to NSCopying as well. For example:

@interface Person (ImmutableValueClass) <NSCopying>
@end

@implementation Person (ImmutableValueClass)

- (NSUInteger)hash {
    return [name_ hash];
}

- (BOOL)isEqual:(id)other {
    Person *otherPerson = other;
    // Using [super isEqual:] to allow easier reparenting
    // -[NSObject isEqual:] is documented as just doing pointer comparison
    return ([super isEqual:otherPerson]
            || ([object isKindOfClass:[self class]]
                && [self.name isEqual:otherPerson.name]
                && [self.dateOfBirth isEqual:otherPerson.dateOfBirth]));
}

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

@end

我在自己的类别中声明了此内容,以便不重复我之前作为示例显示的所有代码,但是在实际代码中,我可能会将所有这些内容放在主@interface@implementation中.请注意,我没有重新声明-hash-isEqual:,我只定义了它们,因为它们已经被NSObject声明了.而且因为这是一个不变的值类,所以我可以完全通过保留self来实现-copyWithZone:,我不需要为该对象制作物理副本,因为它的行为应该完全相同.

I declared this in its own category so as to not repeat all of the code I previously showed as an example, but in real code I would probably put all of this in the main @interface and @implementation. Note that I didn't redeclare -hash and -isEqual:, I only defined them, because they're already declared by NSObject. And that because this is an immutable value class, I can implement -copyWithZone: purely by retaining self, I don't need to make a physical copy of the object because it should behave exactly the same.

但是,如果您使用的是Core Data,请不要这样做. Core Data为您实现了对象唯一化,因此您绝不能具有自己的-hash-isEqual:实现.而且从很好的方面来说,您也不应该真正在核心数据NSManagedObject子类中遵守NSCopying.需要仔细考虑复制"作为Core Data对象图一部分的对象的含义,并且通常这更像是控制器级别的行为.

If you're using Core Data, however, don't do this; Core Data implements object uniquing for you, so you must not have your own -hash or -isEqual: implementation. And for good measure you shouldn't really conform to NSCopying in Core Data NSManagedObject subclasses either; what it means to "copy" objects that are part of a Core Data object graph requires careful thought, and is generally more of a controller-level behavior.

这篇关于Objective-C中的不可变对象:大的init方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆