В чем разница между иварами и свойствами в Objective-C - PullRequest
80 голосов
/ 13 ноября 2010

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

Ответы [ 2 ]

56 голосов
/ 13 ноября 2010

Число 1 отличается от двух других прямым объявлением класса MyOtherObject, чтобы минимизировать объем кода, видимого компилятором и компоновщиком, а также потенциально избежать циклических ссылок. Если вы делаете это таким образом, не забудьте поместить #import в файл .m.

Объявляя @property (и сопоставляя @synthesize в .m) файле, вы автоматически генерируете методы доступа с семантикой памяти, обработанной так, как вы укажете. Основное правило для большинства объектов - Retain, но, например, NSStrings должен использовать Copy. Принимая во внимание, что Singletons и Делегаты обычно должны использовать 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 объявят ивар для вас также, за кулисами. Я считаю, что это новое для Mac OS X - Snow Leopard и iOS4.

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

Я все покрыл?

16 голосов
/ 05 августа 2012

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

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

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

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

...