У чому різниця між ivars та властивостями у Objective-C


82

Яка семантична різниця між цими 3 способами використання ivars та властивостей у Objective-C?

1.

@class MyOtherObject; 
@interface MyObject {
}
@property (nonatomic, retain) MyOtherObject *otherObj;

2.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}
@property (nonatomic, retain) MyOtherObject *otherObj;

3.

#import "MyOtherObject.h"
@interface MyObject {
    MyOtherObject *otherObj;
}

Відповіді:


57

Номер 1 відрізняється від решти двох попереднім оголошенням класу MyOtherObject, щоб мінімізувати кількість коду, побаченого компілятором і компонувальником, а також потенційно уникати кругових посилань. Якщо ви робите це таким чином, не забудьте вставити #import у файл .m.

Оголошуючи @property, (і збігаючи @synthesize у файлі .m), ви автоматично генеруєте методи доступу з використанням семантики пам'яті, як ви вказуєте. Емальним правилом для більшості об’єктів є Retain, але NSStrings, наприклад, повинен використовувати Copy. Тоді як одноосібники та представники зазвичай повинні використовувати Assign. Доступ до рукописного тексту є нудним та схильним до помилок, тому це економить багато набору тексту та безглуздих помилок.

Крім того, оголошення синтезованого властивості дозволяє вам викликати метод доступу, використовуючи крапкові позначення, як це:

self.otherObj = someOtherNewObject; // set it  
MyOtherObject *thingee = self.otherObj; // get it 

Замість звичайного способу передачі повідомлень:

[self setOtherObject:someOtherNewObject]; // set it
MyOtherObject *thingee = [self otherObj]; // get it 

За лаштунками ви дійсно називаєте метод, який виглядає так:

- (void) setOtherObj:(MyOtherObject *)anOtherObject {

    if (otherObject == anOtherObject) {
        return;  
    }

    MyOtherObject *oldOtherObject = otherObject; // keep a reference to the old value for a second
    otherObject = [anOtherObject retain]; // put the new value in  
    [oldOtherObject release]; // let go of the old object
} // set it

... або це

- (MyOtherObject *) otherObject {  
    return otherObject;
} // get it

Тотальний біль у попі, праворуч. Тепер зробіть це для кожного івара в класі. Якщо ви робите це не зовсім правильно, ви отримуєте витік пам'яті. Найкраще просто дозволити компілятору виконувати роботу.

Я бачу, що номер 1 не має івару. Якщо припустити, що це не друкарська помилка, це добре, оскільки директиви @property / @synthesize також оголосять ivar для вас, і за кадром. Я вважаю, що це нове для Mac OS X - Snow Leopard та iOS4.

У номері 3 не створено цих аксесуарів, тому вам доведеться писати їх самостійно. Якщо ви хочете, щоб ваші методи доступу мали побічні ефекти, ви виконуєте свій стандартний танець управління пам'яттю, як показано вище, а потім виконуйте будь-яку необхідну побічну роботу всередині методу доступу. Якщо ви синтезуєте властивість , а також пишете свою , тоді ваша версія має пріоритет.

Я все покрив?


так, велике спасибі! Одним зауваженням, яке я хотів би зробити, є те, що якщо ви вилучите прагму прямого класу в №1 і заміните її #import "MyOtherObject", ви отримаєте помилку часу компіляції, але не впевнені, чому ...
ennuikiller

чи є якась перевага використання підходу №2 перед підходом №1?
Грег

Метод @Greg №1 запобіжить кругові посилання. Див stackoverflow.com/questions/7221174 / ...
willc2

3
Приємна відповідь, за винятком трохи про крапкові позначення. Вам не потрібно синтезувати властивість, щоб використовувати його для позначення крапками. Насправді вам не потрібно декларувати майно взагалі. Поки у вас є заявлені сеттер і геттер (наприклад, setFoo:і foo), ви можете використовувати крапкові позначення.
JeremyP

Для актуальності, якщо використовується ARC, синтез виконується автоматично.
Шон Ларкін

17

Ще за старих часів у вас були ivars, і якщо ви хотіли дозволити іншому класу встановлювати або читати їх, то вам потрібно було визначити getter (тобто, -(NSString *)foo)і setter (тобто, -(void)setFoo:(NSString *)aFoo;)).

Які властивості вам дають - це сетер і геттер безкоштовно (майже!) Разом із іваром. Отже, коли ви визначаєте властивість зараз, ви можете встановити атомність (чи хочете ви дозволити кілька дій налаштування з декількох потоків, наприклад), а також призначити / зберегти / скопіювати семантику (тобто, якщо сеттер копіює нове значення або просто зберегти поточне значення - важливо, якщо інший клас намагається встановити властивість вашого рядка за допомогою змінного рядка, який згодом може бути змінений).

Це те, що @synthesizeробить. Багато людей залишають ім'я ivar однаковим, але ви можете змінити його, коли пишете оператор синтезу (тобто @synthesize foo=_foo;означає зробити ivar з іменем _fooдля властивості foo, тому, якщо ви хочете прочитати або написати це властивість, але не використовуєте self.foo, ви будете доводиться використовувати_foo = ... - це просто допомагає вам ловити прямі посилання на ivar, якщо ви хотіли пройти лише через сеттер та геттер).

Починаючи з Xcode 4.6, вам не потрібно використовувати @synthesizeоператор - компілятор зробить це автоматично і за замовчуванням додасть ім'я ivar до _.


1
Слід зазначити, що атомність властивості не гарантує безпеку ниток .
АТК

Отже, якщо у мене є ivar, який є атомним, ви маєте на увазі, що, поки сеттер встановлює його або отримує його, інший потік вмикається і намагається зробити те або інше, що все це затягується? Тоді в чому сенс атомної? Я розумію, що atomic принаймні гарантує, що якщо ви встановите ivar, він встановлюється, його кількість утримувань є відповідною і т. Д. Інакше чому atomic? [Не те, що він вирішує всі проблеми, просто заважає тобі роздутись]
Девід Х,

2
Ви гарантовано отримаєте дійсний цілий об’єкт - гетер не поверне об’єкт, який перебуває у процесі вивільнення, але якщо інший потік використовує сеттер, ви можете отримати значення до або після. Вказівка ​​на те, що потрібно обробляти поза геттерами та сеттерами. Іншими словами, жоден потік не буде перерваний під час роботи геттера або сеттера, але порядок операцій не визначений (не може бути визначений на цьому рівні AFAIK).
АТК

Ну, я б стверджував, що ваш оригінальний коментар був втрачений - атомність шанується, просто доступ через потоки може призвести до безлічі проблем - отже, кожен ivar, який я коли-небудь оголошував, є атомним, і якщо є потоки, то паралельність вирішується в інших місцях.
David H
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.