инициализатор, свойства, методы доступа и копирование / сохранение / только чтение - PullRequest
7 голосов
/ 28 октября 2010

Хочу понять, как установить параметры свойств (аксессоров).

Я взял следующий код из примера календаря Кал.

// Holiday.h

@interface Holiday : NSObject
{
  NSDate *date;
  NSString *name;
  NSString *country;
}

@property (nonatomic, retain, readonly) NSDate *date;
@property (nonatomic, retain, readonly) NSString *name;
@property (nonatomic, retain, readonly) NSString *country;

- (id)initWithName:(NSString *)name country:(NSString *)country date:(NSDate *)date;

@end

// Holiday.m

#import "Holiday.h"

@implementation Holiday

@synthesize date, name, country;

- (id)initWithName:(NSString *)aName country:(NSString *)aCountry date:(NSDate *)aDate
{
  if ((self = [super init])) {
    name = [aName copy];
    country = [aCountry copy];
    date = [aDate retain];
  }
  return self;
}

- (void)dealloc
{
  [date release];
  [name release];
  [country release];
  [super dealloc];
}

@end

1) Для свойств установлено значение retain, но, поскольку сеттер нельзя использовать, retain здесь не имеет смысла.

2) Кроме того, в методе initWithName значения устанавливаются с помощью copy. Почему бы не определить свойства напрямую с помощью copy и не использовать методы доступа?

@property (nonatomic, copy) NSString *name;
// ...
self.name = aName;

3) Мне нужен readonly здесь? Я не знаю, почему они используются здесь. Если бы я использовал copy вместе с установщиком, readonly запрещает мне устанавливать значение, потому что нет установщика.

4) В методе initWithName иногда используется copy, а иногда retain. Я бы предложил всегда использовать copy здесь, потому что значение не должно изменяться позже.

5) Что я могу вспомнить, так это то, что можно copy / retain в initWithName и release в методе dealloc.

Так как бы вы предложили использовать retain, copy и readonly в этом примере здесь?

1 Ответ

13 голосов
/ 28 октября 2010

ETA: @DougW правильно указывает, что тип собственности свойства (assign / retain / copy) не влияет на получателя.Это все еще влияет на сеттер.Для типов readonly это имеет значение, если вы собираетесь переопределить часть readonly объявления в расширении класса, чтобы вы могли использовать установщик в своей реализации.Переопределение свойства расширения класса допускается только для изменения статуса readonly свойства, поэтому остальная его часть - что означает атомарность и типы владения - должна быть надлежащим образом объявлена ​​в заголовке.Даже если вы не переопределяете свойство сейчас, вы можете сделать это в будущем, поэтому вы можете также задокументировать, как вы хотите управлять памятью для себя, используя правильный параметр для начала.

Автоматический подсчет ссылок (ARC) изменяет подробности реализации среды выполнения, накладывая свои собственные правила управления памятью поверх классических правил пересчета, но правила и рекомендации по настройке ваших свойств остаются прежними.


Зачем использовать retain with readonly? Если вы пометите свойство как retain, синтезированный метод доступа сделает что-то вроде этого:

/* getter for retain property */
- (NSString *)name {
    return [[name retain] autorelease];
}

Теперь, если объект, который вы отправили -name, меняет имяпока вы все еще используете его, вызывающий код будет по-прежнему иметь действительную ссылку на строку.Однако если вы объявите его как assign, это будет выглядеть так:

/* getter for assign property */
- (NSString *)name {
    return name;
}

Теперь, как только объект изменит имя, он должен быть освобожден, чтобы избежать утечки, чтосделает недействительной ссылку на код вызова.retain / copy / assign действительно заявляет о политике управления памятью: retain / copy говорит: «Я обещаю, что у меня есть ссылка на оригинал / копию значения, которое я предоставляю здесь, "в то время как assign говорит:" У меня просто есть значение, и я не требую ссылки на него. "

Когда значение не требует управления памятью, например, int, тогда assign имеет смысл.Если вы намеренно не сохраняете объект, такой как делегат, то assign имеет смысл.Но в большинстве других случаев вам понадобится retain или copy.

Кроме того, файл реализации может переопределять только часть readwrite / readonly объявления свойства, а не памятьчасть управления.Как объявлено, файл .m может иметь:

@interface Holiday (/*class extension*/)
@property(nonatomic, retain, readwrite) NSDate *date;
/* override other properties to make them readwrite... */
@end

Непубличные установщики для объявлений переопределенных свойств будут затем синтезированы вместе с общедоступными средствами доступа.

Почему бы и нетиспользовать сеттеры / аксессоры во время -init? Поскольку сеттеры / аксессоры часто выполняют уведомление KVO, чего следует избегать, пока ваш объект не полностью инициализирован, то есть во время -init (когда он наполовину инициализируется на пути кполная инициализация) и -dealloc (когда он наполовину инициализируется на пути к полной неинициализации).

Зачем использовать copy с readonly? Как в ответ наВаш первый вопрос: потому что если copy против retain против assign влияет как на сеттеры, так и на геттеры.Получатель копии будет выглядеть так:

/* getter for copy property */
- (NSString *)name {
    return [[name copy] autorelease];
}

Почему иногда copy, а иногда retain? copy обычно используется с объектами-значениями (пассивные объекты, представляющие значение);retain обычно используется с другими объектами.Иногда возникают проблемы с эффективностью (скорее всего, преждевременно ...), и вы можете использовать retain там, где обычно используете copy.

Как бы вы использовали copy/ retain вместе с readonly здесь? Почти так же, как они.Я бы переопределил объявления в расширении класса, чтобы я мог использовать сеттеры для изменения значений свойств вне -init и -dealloc, где я использовал бы только прямой доступ к переменным экземпляра.Я также nil извлеку ивары после их выпуска в -dealloc, например,

[name release], name = nil;

Это помогает избежать отправки сообщений или иных ссылок на уже выпущенный объект.

...