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

Какова семантическая разница между этими тремя способами использования 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;
    }

Ответ 1

Номер 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

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

Я вижу, что Номер 1 не имеет ivar. Предполагая, что это не опечатка, это прекрасно, потому что директивы @property/@synthesize объявят вам ивуар для вас, за кулисами. Я считаю, что это ново для Mac OS X - Snow Leopard и iOS4.

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

Я покрыл все?

Ответ 2

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

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

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

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