Когда следует явно использовать @synthesize?

81

Насколько мне известно, начиная с XCode 4.4, средства @synthesizeдоступа к свойствам автоматически генерируются. Но только что я прочитал пример кода NSUndoManagerи заметил, что в нем @synthesizeявно добавлено. Подобно:

@interface RootViewController  ()

@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@property (nonatomic, strong) NSUndoManager *undoManager;

@end

@implementation RootViewController
//Must explicitly synthesize this
@synthesize undoManager;

Я сейчас озадачен ... Когда я должен @synthesizeявно добавить в свой код?

罗泽轩
источник
1
Пример кода может быть старым. В основном, используйте его, если это не превращается в проблему (например, свойства в делегатах не будут автоматически синтезироваться)
borrrden
1
Попробуйте закомментировать файл @sythesize. Если код все еще работает, то в этом нет необходимости.
ThomasW 05

Ответы:

171

Ответов много, но есть большая путаница. Попробую навести какой-то порядок (или бардак увеличивать, посмотрим ...)

  1. Перестанем говорить о Xcode. Xcode - это IDE . clang - это компилятор . Эта функция, которую мы обсуждаем, называется автосинтезом свойств, и это расширение языка Objective-C, поддерживаемое clang , который является компилятором по умолчанию, используемым Xcode.
    Чтобы было ясно, если вы переключитесь на gcc в Xcode, вы не получите преимуществ от этой функции (независимо от версии Xcode). Точно так же, если вы используете текстовый редактор и компилируете с помощью clang из командной строки, вы будут.

  2. Благодаря автосинтезу вам не нужно явно синтезировать свойство, поскольку оно будет автоматически синтезировано компилятором как

    @synthesize propertyName = _propertyName
    

    Однако есть несколько исключений:

    • readwrite свойство с настраиваемым получателем и сеттером

      при обеспечении как геттер и сеттер пользовательской реализации, свойство не будет автоматически синтезированными

    • readonly свойство с настраиваемым получателем

      при предоставлении пользовательской реализации геттера для свойства только для чтения это не будет автоматически синтезироваться

    • @dynamic

      при использовании @dynamic propertyNameсвойство не будет автоматически синтезироваться (довольно очевидно, поскольку @dynamicи @synthesizeявляются взаимоисключающими)

    • свойства, объявленные в @protocol

      при согласовании с протоколом любое свойство, определяемое протоколом, не будет автоматически синтезировано

    • недвижимость, объявленная в категории

      это случай, когда @synthesizeдиректива не вставляется автоматически компилятором, но эти свойства также нельзя синтезировать вручную. Хотя категории могут объявлять свойства, их вообще нельзя синтезировать, поскольку категории не могут создавать ivars. Для полноты картины я добавлю, что еще можно подделать синтез свойств с помощью среды выполнения Objective-C .

    • переопределенные свойства (новое с clang-600.0.51, доставка с Xcode 6, спасибо Marc Schlüpmann)

      когда вы переопределяете свойство суперкласса, вы должны явно синтезировать его

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

За исключением последних трех случаев, общая философия заключается в том, что всякий раз, когда вы вручную указываете всю информацию о свойстве (путем реализации всех методов доступа или использования @dynamic), компилятор будет предполагать, что вам нужен полный контроль над свойством, и отключит автосинтез на Это.

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

Габриэле Петронелла
источник
Если автор вопроса все еще здесь, я думаю, что этот ответ следует принять. Он самый полный.
Стивен Фишер
3
Свойства, указанные в категории, тоже не синтезируются автоматически, насколько я помню. (Основная причина в том, что вы не можете добавить переменную экземпляра в категорию.)
Мартин Р.
@MartinR, хорошее замечание. Они не синтезируются автоматически, но они также не могут быть синтезированы вручную, так как @synthesizeв категории это запрещено (как вы уже отметили, в категориях отсутствует ivar). Добавлю примечание.
Габриэле Петронелла
Но этот ответ не отвечает на конкретный вопрос: КОГДА следует использовать @synthesize? Всегда, никогда, только при соблюдении определенных условий?
Джефф
21

Если вы не используете явно, @synthesizeкомпилятор поймет ваше свойство так же, как если бы вы написали

@synthesize undoManager=_undoManager;

тогда вы сможете писать в своем коде такие вещи, как:

[_undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Это общепринятое соглашение.

если вы напишете

@synthesize undoManager;

у вас будет:

[undoManager doSomething]; // iVar
[self.undoManager doSomethingElse]; // Use generated getter

Лично я прекращаю использовать @synthesize, так как это больше не обязательно. Для меня единственная причина использовать @synthesize- это привязать iVarк @property. Если вы хотите создать для него определенные геттеры и сеттеры. Но в данном куске кода нет iVar, думаю, что это @synthesizeбесполезно. Но теперь я думаю, что новый вопрос - «Когда использовать iVar?», И у меня нет другого ответа, кроме «никогда» на этот!

KIDdAe
источник
2
Согласен, прекратил пользоваться, так @synthesizeкак больше нет причин. Кроме того, начальное подчеркивание служит хорошим визуальным флажком, чтобы вы знали, что работаете с сетью безопасности управления памятью и потоками, которую обеспечивают свойства (которые вы должны пропустить initи deallocметоды).
BergQuester 05
1
Вопрос был в том, когда нужно явно синтезировать. Вы вообще на это не ответили.
Fogmeister 05
1
Сегодня я обнаружил, что это не совсем так. Без @synthesize вы несете ответственность за создание резервной переменной экземпляра. С его помощью компилятор сделает это за вас.
Стивен Фишер
1
И я никого не голосовал за это открытие. :)
Стивен Фишер
1
-1 вы полностью упускаете случаи, когда вам нужен явный синтез. Также это функция компилятора, а не Xcode.
Габриэле Петронелла
14

Когда я должен @synthesizeявно добавить в свой код?

Как правило, если это требуется: вы, вероятно, никогда не попадете в дело, где это необходимо.

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

Предположим, вы пишете как собственный метод получения, так и метод установки, но хотите, чтобы его поддерживала переменная экземпляра. (Для атомарного свойства это так же просто, как потребность в настраиваемом сеттере: компилятор напишет геттер, если вы укажете сеттер для одноатомного свойства, но не для атомарного свойства.)

Учти это:

@interface MyObject:NSObject
@property (copy) NSString *title;
@end

@implementation MyObject

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Это не сработает, потому _titleчто не существует. Вы указали и геттер, и сеттер, поэтому Xcode (правильно) не создает для него переменную резервного экземпляра.

введите описание изображения здесь

У вас есть два варианта, как заставить его существовать. Вы можете изменить на @implementationэто:

@implementation MyObject {
    NSString *_title;
}

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Или измените его на это:

@implementation MyObject

@synthesize title = _title;

- (NSString *)title {
    return _title;
}
- (void)setTitle:(NSString *)title {
    _title = [title copy];
}

@end

Другими словами, хотя для практических целей синтезировать никогда не требуется *, его можно использовать для определения переменных экземпляра, поддерживающих свойства, когда вы предоставляете геттер / сеттер. Вы можете решить, какую форму здесь вы хотите использовать.

Раньше я предпочитал указывать переменную экземпляра в @implementation {}, но теперь думаю, что @synthesizeмаршрут - лучший выбор, поскольку он удаляет избыточный тип и явно связывает поддерживающую переменную со свойством:

  1. Измените тип свойства, и тип переменной экземпляра изменится.
  2. Измените его квалификатор хранилища (например, сделайте его слабым вместо сильного или сильным вместо слабого), и квалификатор хранилища изменится.
  3. Удалите или переименуйте свойство, и @synthesizeкомпилятор выдаст ошибку. Вы не получите случайных переменных экземпляра.

* -Я знаю один случай, когда это было необходимо, относящееся к разделению функциональности по категориям в нескольких файлах. И я не удивлюсь, если Apple исправит это или даже уже исправит.

Стивен Фишер
источник
Вы уверены? Вы даже пробовали? Я написал много настраиваемых сеттеров и геттеров и НИКОГДА не синтезировал свои свойства. (И я использовал ваш первый пример, который, как вы сказали, не работает)
Marc
Да, я уверен. Код был скопирован из нового проекта. Он не будет работать в последних версиях Xcode, если вы не укажете в свойстве nonatomic.
Стивен Фишер,
1
да, это правда .. но 99% времени ваши свойства неатомарны. Так что только в том редком случае, когда ваше свойство является атомарным и вам нужен пользовательский геттер / сеттер, который вам действительно нужно синтезировать.
Marc
Если вы предоставляете настраиваемую реализацию как для установщика, так и для получателя, то компилятор будет считать, что вы берете на себя управление свойством, и не будет синтезировать его за вас.
Габриэле Петронелла
2
Я согласен с вами в этом, хотя я могу понять, почему Apple изначально подумала, что это хорошая идея. Настоящая проблема заключается в том, что atomicон не был добавлен в качестве спецификатора свойства позже. Теперь, когда он там, вы можете найти предупреждающий флаг CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES.
Стивен Фишер
7

Хорошо, когда вы создаете недвижимость ...

@property NSString *name;

Xcode автоматически синтезирует iVar, как если бы вы написали ...

@synthesize name = _name;

Это означает, что вы можете получить доступ к собственности с помощью ...

self.name;
// or
_name;

Любой из них будет работать, но на self.nameсамом деле использует только методы доступа.

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

У вас все в порядке, если вы просто переопределите установщик или просто переопределите получатель. Но если вы сделаете и то, и другое, компилятор этого не поймет, и вам придется синтезировать это вручную.

Тем не менее, как правило.

Не делайте iVars. Просто используйте свойство. Не синтезируйте это.

Fogmeister
источник
1
Есть еще случаи, когда автосинтез не выполняется. Ознакомьтесь с моим ответом.
Габриэле Петронелла
@GabrielePetronella, можете ли вы кратко перечислить эти случаи для среднего читателя?
Дэн Розенстарк
@DanRosenstark сейчас это не имеет отношения к любому программированию. Вам действительно стоит использовать Swift. И TBH, я думаю, что в ObjC больше нет ничего общего с синтезом. Если только вы не работаете над кодовой базой старше 5 лет.
Fogmeister
@Fogmeister да, это все еще актуально в случае, если вы упоминаете в своем ответе (где вы переопределяете как сеттер, так и получатель). А что касается Objective-C, который «не имеет отношения к программированию сейчас», пожалуйста, позвоните мне на работу и сообщите им. Также сообщите Facebook, Google и Apple, которые используют Objective-C для внутренних целей.
Дэн Розенстарк
Я уверен, что это правда без Quora, но в любом случае: quora.com/…
Дэн Розенстарк
1

Синтез свойств требуется, когда свойство объявляется в протоколе. Он не будет автоматически синтезироваться в интерфейсе реализации.

Лео Натан
источник
правда, но это всего лишь один случай. Возможно, вы захотите уточнить детали.
Габриэле Петронелла
@GabrielePetronella Это единственное, о чем я могу думать в столь поздний час. =]
Leo Natan
0

Спасибо, что разъяснили это. У меня была аналогичная проблема.

@synthesize firstAsset, secondAsset, audioAsset;
@synthesize activityView;

Итак, теперь, закомментировав их, я просмотрел и заменил каждое вхождение, например,

self.firstAsset Кажется, я мог бы также использовать firstAsset, но обнаружил, что слишком часто скучаю по значку " ".

Гарри Макговерн
источник
-1

Xcode не требует явного @synthesizeобъявления.

Если вы не пишете, @synthesizeэто то же самое, что и делать:

@synthesize manager = _manager;

Образец кода мог быть старым. Скоро они его обновят.

Вы можете получить доступ к своим свойствам, например:

[self.manager function];

Это рекомендованное Apple соглашение. Я слежу за ним и рекомендую вам тоже!

Сэм Фишер
источник
2
Оператор [_manager function]НЕ будет обращаться к свойству, вместо этого он будет напрямую обращаться к базовому ivar .
CouchDeveloper
@CouchDeveloper Если вы придираетесь к мелочам, будьте точны. Имущество состоит из нескольких вещей, включая ивар. Так что лучше сказать, что [_manager function]не использует аксессоры свойства.
Николай Рухе
@CouchDeveloper - Спасибо за это! Починил это! :)
Сэм Фишер
1
@NikolaiRuhe Ты прав, Николай, надо было быть точнее. ;)
CouchDeveloper 05