Использование переменных экземпляра с Modern Runtime - PullRequest
9 голосов
/ 17 июня 2009

У меня есть несколько лет опыта работы в Obj-c и Cocoa, но я только сейчас возвращаюсь к нему и достижениям Obj-C 2.0 и т. Д.

Я пытаюсь осмыслить современную среду выполнения и объявить свойства и т. Д. Одна вещь, которая меня немного смущает, - это способность в современной среде выполнения создавать неявные iVars. И, конечно, это подразумевает, что в вашем коде вы всегда должны использовать self.property для доступа к значению.

Однако в методах init * и dealloc (при условии, что вы не используете GC) мы должны использовать iVar напрямую (в текущей среде выполнения).

Итак, вопросы:

  1. Должны ли мы использовать средства доступа к свойствам в init * и dealloc с Modern Runtime?

  2. Если так , почему это отличается? Это только потому, что компилятор не видит iVar?

  3. Если мне нужно переопределить метод доступа, могу ли я по-прежнему обращаться к тому iVar, который будет определен во время выполнения, или мне нужно определить фактический iVar, который затем будет использовать среда выполнения?

  4. Опять же, , если я могу получить доступ к синтезированному iVar , почему я не могу продолжать делать это для методов init * и dealloc?

Я несколько раз читал документы, но они показались мне немного расплывчатыми, и я хочу быть уверенным, что хорошо понимаю их, чтобы решить, как продолжить кодирование.

Надеюсь, мои вопросы понятны.


Краткий обзор испытаний :

  1. Если вы не объявите ivar в устаревшем виде, компилятор будет полностью недоволен

  2. Если вы используете #ifndef __OBJC2__ вокруг ivar в устаревшем компиляторе, то вы счастливы, и вы можете использовать как ivar напрямую, так и как свойство

  3. В современной среде выполнения вы можете оставить ivar неопределенным и получить доступ как свойство

  4. В современной среде выполнения попытка прямого доступа к ivar без объявления приводит к ошибке во время компиляции

  5. @private декларация ivar, конечно, позволяет прямой доступ к ivar, как в устаревшем, так и в современном

Разве это не дает верного пути для продвижения прямо сейчас?

Ответы [ 5 ]

10 голосов
/ 17 июня 2009

В текущем (OS X 10.5 / GCC 4.0.1) компиляторе вы не можете получить прямой доступ к синтезированным во время выполнения ivars. Грег Паркер (Greg Parker), один из инженеров среды выполнения OS X, поместил это этим способом в список разработчиков какао (12 марта 2009 г.):

Вы не можете в текущем компиляторе. будущий компилятор должен это исправить. использование явные @private ivars в То время. @Private ivar не должен считаться частью договора - это то, что означает @private, принудительно по предупреждениям компилятора и компоновщику ошибки.

И почему нет способа явно объявить переменные экземпляра в файле .m для новой среды выполнения?

Три причины: (1) есть некоторые нетривиальные детали дизайна для работы (2) часы компилятора и (3) @private ivars как правило, достаточно хорошо.

Итак, сейчас вы должны использовать точечную нотацию для доступа к свойствам, даже в init и dealloc. Это противоречит лучшей практике использования иваров напрямую в этих случаях, но нет никакого способа обойти это. Я считаю, что простота использования синтезированных во время выполнения иваров (и преимущества производительности) перевешивает это в большинстве случаев. Там, где вам нужен прямой доступ к ivar, вы можете использовать @private ivar, как предлагает Грег Паркер (ничто не мешает вам смешивать явно объявленные и синтезированные во время выполнения ivars).

Обновление В OS X 10.6 64-разрядная среда выполнения обеспечивает прямой доступ к синтезированным иварам через self->ivar.

1 голос
/ 17 июня 2009

Поскольку сами переменные экземпляра могут быть синтезированы только в современной среде выполнения (и должны быть объявлены в @interface в 32-битном или до Leopard), самым безопасным / наиболее переносимым также является объявление ivar

  • Должны ли мы использовать средства доступа к свойствам в init * и dealloc с Modern Runtime?

Мое эмпирическое правило «возможно» для -init* и «обычно нет» для -dealloc.

При инициализации объекта вы должны убедиться, что правильно копируете / сохраняете значения для иваров. Если метод установки свойства не имеет какого-либо побочного эффекта, который делает его неприемлемым для инициализации, определенно используйте абстракцию, предоставляемую свойством.

При освобождении объекта вы хотите освободить любые объекты ивара, но не хранить новые. Простой способ сделать это - установить для свойства значение nil (myObject.myIvar = nil), которое в основном вызывает [myObject setMyIvar:nil]. Поскольку сообщения на ноль игнорируются, в этом нет никакой опасности. Тем не менее, это слишком, когда [myIvar release]; это обычно все, что вам нужно. В общем, не используйте свойство (или непосредственно метод set) в ситуациях, когда освобождение должно вести себя иначе, чем установка переменной.

Я могу понять аргумент eJames против использования методов доступа к свойствам в init / dealloc, но с другой стороны, если вы измените поведение свойства (например, измените с сохранения на копирование или просто назначьте без сохранения) и не t использовать его в init, или наоборот, поведение также может быть не синхронизировано. Если инициализация и изменение ивара должны действовать одинаково, используйте метод доступа к свойствам для обоих.

  • Если так, то чем это отличается? Это только потому, что компилятор не видит ivar?

Современная среда выполнения более разумно относится к размеру и разметке классов, поэтому вы можете изменять разметку ivars без необходимости перекомпиляции подклассов. Он также может вывести имя и тип нужного вам ивара по названию и типу соответствующего свойства. Руководство по программированию во время выполнения Objective-C 2.0 *1028* содержит больше информации, но, опять же, я не знаю, насколько подробно там объяснены детали.

  • Если мне нужно переопределить метод доступа, могу ли я по-прежнему обращаться к тому iVar, который будет определен во время выполнения, или мне нужно определить фактический iVar, который затем будет использовать среда выполнения?

Я не проверял это, но я считаю, что вам разрешен доступ к названному ivar в коде, так как он действительно должен быть создан. Я не уверен, будет ли компилятор жаловаться, но я предполагаю, что, поскольку он позволит вам синтезировать ивар без каких-либо жалоб, он также достаточно умен, чтобы знать о синтезированном иваре и позволяет вам ссылаться на него по имени.

  • Опять же, если я могу получить доступ к синтезированному iVar, почему я не могу продолжать делать это для методов init * и dealloc?

Вы должны иметь доступ к свойству и / или ivar в любое время после выделения экземпляра.

0 голосов
/ 04 марта 2010

Я относительно новичок в Obj-C (но не в программировании), и меня также смутила эта тема.

Аспект, который меня беспокоит, заключается в том, что, по-видимому, относительно легко непреднамеренно использовать iVar вместо свойства. Например, написание:

myProp = someObject;

вместо

self.myProp = someObject;

По общему признанию, это «пользовательская» ошибка, но все же кажется, что это довольно легко сделать случайно в некотором коде, и для сохраненного или атомарного свойства это может предположительно привести к проблемам.

В идеале я бы предпочел, чтобы среда выполнения могла применять какой-либо шаблон к имени свойства при генерации любого iVar. Например. всегда ставьте перед ними префикс "_".

На практике в настоящее время я делаю это вручную - явно объявляю свои ивары и намеренно даю им разные имена из свойств. Я использую префикс «m» в старом стиле, поэтому если мое свойство «myProp», то мой iVar будет «mMyProp». Затем я использую @synthesize myProp = mMyProp, чтобы связать их.

Я допускаю, что это немного неуклюже и требует дополнительной типизации, но мне кажется, что стоит немного яснее разобраться в коде. Конечно, я все еще могу ошибиться и набрать mMyProp = someObject, но я надеюсь, что префикс «m» предупредит меня о моей ошибке.

Было бы намного приятнее, если бы я мог просто объявить свойство и позволить компилятору / среде выполнения делать все остальное, но когда у меня много кода, мой инстинкт инстинкта подсказывает мне, что я буду ошибаться таким образом, если мне все еще придется следуйте ручным правилам для init / dealloc.

Конечно, есть и много других вещей, которые я могу сделать неправильно ...

0 голосов
/ 26 июля 2009

У меня такая же проблема. У меня не получается получить доступ к синтезированным переменным экземпляра следующим образом:

публичный заголовок

@interface MyObject:NSObject {
}
@property (retain) id instanceVar;
@property (retain) id customizedVar;
@end

закрытый заголовок / реализация

@interface MyObject()
@property (retain) id storedCustomizedVar;
@end

@implementation MyObject
@synthesize instanceVar, storedCustomizedVar;
@dynamic customizedVar;

- customizedVar {
  if(!self.storedCustomizedVar) {
    id newCustomizedVar;
    //... do something
    self.storedCustomizedVar= newCustomizedVar;
  }
  return self.storedCustomizedVar;
}

- (void) setCustomizedVar:aVar {
  self.storedCustomizedVar=aVar;
}

@end

Это не так элегантно, но, по крайней мере, он поддерживает мой публичный заголовочный файл в чистоте.

Если вы используете KVO, вам нужно определить customVar в качестве зависимого ключа для хранимой переменной customCustomizedVar.

0 голосов
/ 17 июня 2009

Существует еще один вопрос SO с аналогичной информацией, но он не совсем дубликат.

Нижняя строка из документации Objective-C 2.0 и цитата из Ответ Марка Бесси следующий:

Существуют различия в поведении, которые зависят от времени выполнения (см. Также «Различия во время выполнения»):

Для устаревших сред выполнения переменные экземпляра уже должны быть объявлены в блоке @interface. Если переменная экземпляра с тем же именем и совместимым типом, что и свойство, существует, она используется - в противном случае вы получите ошибку компилятора.

Для современных сред выполнения переменные экземпляра синтезируются по мере необходимости. Если переменная экземпляра с таким именем уже существует, она используется.

Мое понимание таково:

Не следует использовать средства доступа к свойствам в методах init* и dealloc по тем же причинам, по которым вы не должны использовать их в устаревшей среде выполнения: он оставляет вас открытыми для потенциальных ошибок, если впоследствии вы переопределите методы свойства, и в конечном итоге делать то, что не следует делать в init* или dealloc.

Вы должны иметь возможность синтезировать ivar и , переопределяя методы свойства следующим образом:

@interface SomeClass
{
}
@property (assign) int someProperty;
@end

@implementation SomeClass
@synthesize someProperty; // this will synthesize the ivar
- (int)someProperty { NSLog(@"getter"); return someProperty; }
- (void)setSomeProperty:(int)newValue
{
    NSLog(@"setter");
    someProperty = newValue;
}
@end

Что заставляет меня думать, что вы сможете получить доступ к синтезированному ивару также в ваших init* и dealloc методах. Единственное, о чем я могу подумать, это то, что строка @synthesize может идти за до определения ваших методов init* и dealloc в исходном файле.

В конце концов, поскольку объявление ивара в интерфейсе все еще работает, это самая безопасная ставка.

...