В чем разница между ivars и свойствами в Objective-C

82

В чем семантическая разница между этими тремя способами использования 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) файле, вы автоматически создаете методы доступа с семантикой памяти, обрабатываемой так, как вы указываете. Практическое правило для большинства объектов - «Сохранить», но, например, для NSStrings следует использовать «Копировать». В то время как синглтоны и делегаты обычно должны использовать 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 не имеет созданных аксессуаров, поэтому вы должны написать их самостоятельно. Если вы хотите, чтобы ваши методы доступа имели побочные эффекты, вы выполняете свой стандартный танец управления памятью, как показано выше, а затем выполняете любую дополнительную работу, которая вам нужна, внутри метода доступа. Если вы синтезируете свойство, а также пишете свое собственное , то ваша версия имеет приоритет.

Я все рассказал?

willc2
источник
да, большое спасибо! Одно замечание, которое я хотел бы сделать, заключается в том, что если вы удалите прагму прямого класса в # 1 и замените ее на #import "MyOtherObject", вы получите ошибку времени компиляции, хотя не знаю почему ....
ennuikiller
есть ли преимущество использования подхода №2 перед подходом №1?
Грег
@Greg Метод №1 предотвратит циклическую ссылку. См stackoverflow.com/questions/7221174/...
willc2
3
Хороший ответ, кроме того, что касается записи через точку. Вам не нужно синтезировать свойство, чтобы использовать его для записи через точку. На самом деле декларировать недвижимость вообще не нужно. Пока у вас есть объявленные сеттер и получатель (например, setFoo:и foo), вы можете использовать точечную нотацию.
JeremyP 05
Для удобства, при использовании ARC синтез выполняется автоматически.
Шон Ларкин
17

Раньше у вас были ivars, и если вы хотели разрешить какому-то другому классу устанавливать или читать их, вам нужно было определить геттер (т. -(NSString *)foo)Е. И сеттер (т. Е. -(void)setFoo:(NSString *)aFoo;).

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

Вот что @synthesizeделает. Многие люди оставляют имя ivar таким же, но вы можете изменить его, когда пишете оператор синтеза (т. @synthesize foo=_foo;Е. Означает создание имени ivar _fooдля свойства foo, поэтому, если вы хотите прочитать или записать это свойство, и вы не используете self.foo, вы будете нужно использовать _foo = ...- это просто помогает вам поймать прямые ссылки на ivar, если вы хотите пройти только через сеттер и геттер).

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

Дэвид Х
источник
1
Следует отметить, что атомарность свойства не гарантирует безопасность потоков .
jscs 05
Итак, если у меня есть ivar, который является атомарным, вы имеете в виду, что пока сеттер устанавливает его или получатель получает его, другой поток вмешивается и пытается сделать то же самое, что все это обманывается? Тогда в чем смысл атомарного? Насколько я понимаю, atomic по крайней мере гарантирует, что если вы установите ivar, он будет установлен, его счетчик сохранения будет подходящим и т. Д. В противном случае, почему атомарный? [Не то, чтобы это решало все проблемы, просто мешает вам развлечься]
Дэвид Х
2
Вы гарантированно получите действительный объект целиком - геттер не вернет объект, который находится в процессе освобождения, но если другие потоки используют сеттер, вы можете получить значение до или после. Указание того, что нужно обрабатывать вне геттеров и сеттеров. Другими словами, ни один поток не будет прерван во время операции получения или установки, но порядок операций не определен (не может быть на этом уровне AFAIK).
jscs 05
Что ж, я бы сказал, что ваш исходный комментарий был неуместным - атомарность соблюдается, просто доступ через потоки может привести к множеству проблем - таким образом, каждый ivar, который я когда-либо объявил, является атомарным, и если есть потоки, тогда параллелизм рассматривается в другом месте.
Дэвид Х