Вы можете иметь общедоступное свойство только для чтения и использовать частное свойство для чтения и записи, чтобы предоставить установщик для свойства в вашем классе, если оно вам действительно нужно.Однако вам следует подумать, нужно ли это вообще.
В качестве примера рассмотрим следующее объявление и определение неизменяемого класса 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
.Это потому, что цель класса - быть неизменным;нет необходимости в установщиках, если переменные экземпляра будут устанавливаться только во время инициализации.
Также обратите внимание, что я объявил переменные экземпляра с именами, отличными от свойств.Это проясняет различие между свойствами как программным интерфейсом класса и переменными экземпляра как подробностью реализации класса.Я видел слишком много разработчиков (особенно новичков в Mac OS X и iOS, включая многих из C #), которые связывают свойства с переменными экземпляра, которые могут использоваться для их реализации.
Третье, на что стоит обратить вниманиеявляется то, что я объявил оба эти свойства как copy
, хотя они только для чтения.Есть две причины.Во-первых, хотя прямые экземпляры этого класса неизменны, ничто не мешает созданию подкласса MutablePerson.На самом деле, это может быть даже желательно!Таким образом, copy
четко определяет ожидания суперкласса - что значения самих свойств name
и 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, не делайте этого;Базовые данные реализуют уникальные объекты для вас, поэтому вы не должны иметь собственную реализацию -hash
или -isEqual:
.И для хорошей меры вы не должны действительно соответствовать NSCopying в подклассах Core Data NSManagedObject;что означает «копировать» объекты, являющиеся частью графа объектов Core Data, требует тщательного обдумывания и, как правило, в большей степени относится к поведению на уровне контроллера.