Вопрос

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

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

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

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

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

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

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

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

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

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


Краткое описание тестирования:

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

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

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

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

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

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

Это было полезно?

Решение

В текущем (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 .

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  

Существуют различия в поведении, которые зависят от времени выполнения (см. также & # 8220; Различия во время выполнения & # 8221;):

     

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

     

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

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

Вы не должны использовать методы доступа к свойствам в методах 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 методы в исходном файле.

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

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

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

@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 в качестве зависимого ключа для элемента StorageCustomizedVar.

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

Меня беспокоит то, что непреднамеренно использовать iVar вместо свойства довольно просто. Например, писать:

myProp = someObject;

вместо

self.myProp = someObject;

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top