Objective-C中的不可变对象:大init方法?
我想在Objective-C中有一个带有不可变字段的Object.
在C#中,我将使用属性与私有setter和一个大的构造函数. 我会在Objective-C中使用什么? 使用@property似乎不允许我将setter声明为private. 运用 initWithData: (NSString*) something createDate: (NSDate*) date userID: (long) uid 如果我要设置超过4个属性,似乎过于冗长. 我会在.h文件中声明getter,只在.m中声明setter吗? 我需要在某些东西和日期上使用保留或复制(顺便说一下:我应该使用这两个中的哪一个?),所以我需要在setter中使用一些代码. 或者甚至还有其他类似不可变关键字的东西? 解决方法
您可以拥有一个公共只读属性,并使用私有读写属性为您的类中的属性提供一个setter,如果您确实需要它.但是,您应该考虑是否有必要.
作为示例,请考虑以下声明和不可变Person类的定义: // 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中声明一个类扩展,它将name和dateOfBirth属性重新声明为readwrite.这是因为该类的目的是不可变的;如果实例变量只在初始化时设置,则不需要设置setter. 另请注意,我声明的实例变量的名称与属性不同.这清楚地表明属性作为类的编程接口和实例变量之间的区别作为类的实现细节.我见过太多的开发人员(特别是那些刚接触Mac OS X和iOS的人,包括很多来自C#的人)将属性与可能用于实现它们的实例变量混淆. 需要注意的第三点是,我将这两个属性都声明为副本,即使它们是只读的.有两个原因.第一个是虽然这个类的直接实例是不可变的,但没有什么能阻止创建一个MutablePerson子类.事实上,这甚至可能是可取的!因此,副本清楚地指明了超类的期望 – 名称和dateOfBirth属性本身的值不会改变.它还暗示-initWithName:dateOfBirth:也可能是副本;它的文档评论应该清楚.其次,NSString和NSDate都是值类;不可变的副本应该很便宜,而且你不想挂在可变子类的实例上,这个子类将从你自己的类下面改变. (现在实际上并没有NSDate的任何可变子类,但这并不意味着有人无法创建自己的……) 最后,不要担心您的指定初始化程序是否冗长.如果对象的实例无效,除非它处于某种特定状态,那么您指定的初始化程序需要将其置于该状态 – 并且需要采用适当的参数来执行此操作. 还有一件事:如果您正在创建这样的不可变值类,您可能还应该实现自己的-isEqual:和-hash方法以进行快速比较,并且可能也符合NSCopying.例如: @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声明了.因为这是一个不可变的值类,我可以实现-copyWithZone:纯粹通过保留self,我不需要制作对象的物理副本,因为它应该表现完全相同. 但是,如果您使用的是Core Data,请不要这样做; Core Data为您实现对象唯一性,因此您不能拥有自己的-hash或-isEqual:实现.为了更好的衡量,你不应该真正符合Core Data NSManagedObject子类中的NSCopying; “复制”作为核心数据对象图的一部分的对象意味着需要仔细考虑,通常更多的是控制器级行为. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |