Коли слід явно використовувати @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

Відповіді:


171

Є багато відповідей, але також велика плутанина. Я спробую навести якийсь порядок (або збільшити безлад, ми побачимо ...)

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

  2. Завдяки автосинтезу не потрібно явно синтезувати властивість, оскільки воно автоматично синтезується компілятором як

    @synthesize propertyName = _propertyName
    

    Однак існує кілька винятків:

    • властивість readwrite за допомогою користувацького геттера та сеттера

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

    • властивість лише для читання із користувацьким геттером

      при наданні власної реалізації getter для властивості лише для читання, це не буде синтезовано автоматично

    • @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
Властивості, зазначені в категорії, також не синтезуються автоматично, наскільки я пам’ятаю. (Основна причина полягає в тому, що ви не можете додати змінну екземпляра в категорію.)
Martin R,

@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на a @property. Якщо ви хочете згенерувати для цього конкретний геттер та сеттер. Але в даному фрагменті коду немає iVar, я думаю, що це @synthesizeмарно. Але зараз я думаю, що нове питання "Коли використовувати iVar?", І я не маю іншої відповіді, крім "ніколи" на це!


2
Погодився, я перестав користуватися, @synthesizeоскільки немає жодних причин робити це більше. Також провідне підкреслення служить приємним візуальним прапором, щоб повідомити, що ви працюєте навколо управління пам’яттю та захисту мережі, що надають властивості (які слід пропустити initта deallocметоди).
BergQuester

1
Питання полягало в тому, коли потрібно чітко синтезувати. Ви взагалі на це не відповіли.
Фогмайстер

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

Іншими словами, хоча синтезувати для практичних цілей ніколи не потрібно *, він може бути використаний для визначення змінних екземпляра, що підтримують властивості, коли ви надаєте getter / setter. Ви можете вирішити, яку форму тут ви хочете використовувати.

Раніше я віддавав перевагу вказівці змінної екземпляра в @implementation {}, але зараз я вважаю, що @synthesizeмаршрут є кращим вибором, оскільки він видаляє надлишковий тип і явно прив'язує змінну-резерв до властивості:

  1. Змініть тип властивості, а тип змінної екземпляра зміниться.
  2. Змініть його кваліфікатор зберігання (наприклад, зробіть його слабким замість сильного або сильним замість слабкого), і кваліфікатор зберігання змінюється.
  3. Видаліть або перейменуйте властивість, і програма @synthesizeстворить помилку компілятора. Ви не отримаєте збиті змінні екземпляра.

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


Ти впевнений? Ви хоч спробували? Я написав багато власних сеттерів та геттерів і НІКОЛИ не синтезував мої властивості. (І я використав ваш перший приклад, який, як ви сказали, не працює)
Марк

Так, я впевнений. Код було скопійовано з нового проекту. Він не працюватиме в останніх версіях Xcode, якщо ви не вказали неатомні властивості.
Стівен Фішер

1
так, це правда .. але у 99% випадків ваші властивості неатомні. Тож лише у тих рідкісних випадках, коли ваша властивість є атомною, і вам потрібен власний геттер / сеттер, який вам фактично потрібно синтезувати.
Марк

Якщо ви надаєте власну реалізацію як для сеттера, так і для геттера, то компілятор припустить, що ви берете під контроль властивість, і він не синтезує його для вас.
Габріеле Петронелла

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. Просто скористайтеся власністю. Не синтезуйте його.


1
Є більше випадків, коли аутосинтез не проводиться. Перевірте мою відповідь.
Габріеле Петронелла

@GabrielePetronella, чи можете ви коротко перерахувати ці випадки для пересічного читача?
Ден

@DanRosenstark це не має значення для тих, хто програмує зараз. Ви дійсно повинні використовувати Swift. І TBH, я думаю, що все це синтез більше навіть не є предметом ObjC. Якщо ви не працюєте над кодовою базою віком від 5 років.
Фогмайстер

@Fogmeister так, це все одно має справу у випадку, про який ви згадали у своїй відповіді (де ви перевизначаєте як сеттера, так і геттера). А щодо того, що Objective-C не є "актуальним для тих, хто програмує зараз", будь ласка, зателефонуйте на мою денну роботу і скажіть їм. Також повідомте Facebook, Google та Apple, які з великим ефектом використовують внутрішньо Objective-C.
Dan Rosenstark,

Я впевнений, що це правда без Quora, але в будь-якому випадку: quora.com/…
Ден

1

Синтез властивостей необхідний, коли властивість оголошено в протоколі. Він не буде автоматично синтезований у реалізованому інтерфейсі.


правда, але це лише один випадок. Можливо, ви захочете детальніше розробити.
Габріеле Петронелла

@GabrielePetronella Це єдиний, кого я можу згадати в цю пізню годину. =]
Лео Натан

0

Дякую за роз'яснення. У мене була подібна проблема.

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

Тож тепер, прокоментувавши їх, я пройшов і замінив кожну появу, наприклад

self.firstAsset Здається, я міг би також використовувати firstAsset, але я виявляю, що сумую надто часто " ".


-1

Xcode не вимагає явного @synthesizeоголошення.

Якщо ви не пишете @synthesizeце так само, як робити:

@synthesize manager = _manager;

Зразок коду міг бути старим. Незабаром оновлять.

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

[self.manager function];

Це рекомендована конвенція Apple. Я дотримуюся цього і рекомендую зробити це теж!


2
Заява [_manager function]НЕ буде доступ до власності, замість цього він буде мати доступ до лежачого в основі Івара безпосередньо .
CouchDeveloper

@CouchDeveloper Якщо ви хочете зачепити, ви повинні бути точними. Власність складається з декількох речей, включаючи івар. Тож краще сказати, що [_manager function]не використовує пристосування для власності.
Микола Руе

@CouchDeveloper - Дякую за це! Виправлено! :)
Сем Фішер

1
@NikolaiRuhe Ви маєте рацію, Микола, я повинен був бути точнішим. ;)
CouchDeveloper
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.