Свойство против переменной экземпляра - PullRequest
33 голосов
/ 06 апреля 2009

Я пытаюсь понять, как некоторые люди используют стратегии, чтобы отличать переменные экземпляров от свойств. Распространенный шаблон выглядит следующим образом:

@interface MyClass : NSObject {
    NSString *_myVar;
}

@property (nonatomic, retain) NSString *myVar;
@end

@implementation MyClass
@synthesize myVar = _myVar;

Теперь я подумал, что вся эта стратегия заключается в том, чтобы можно было легко различить разницу между иваром и собственностью. Итак, если я хочу использовать управление памятью, унаследованное от синтезированного свойства, я бы использовал что-то вроде:

myVar = @"Foo";

Другой способ будет ссылаться на него через себя. [Ivar / property here].

Проблема с использованием стратегии @synthesize myVar = _myVar заключается в том, что я решил, что при написании такого кода, как:

myVar = some_other_object; // doesn't work. 

Компилятор жалуется, что myVar не объявлен. Почему это так?

Спасибо.

Ответы [ 7 ]

28 голосов
/ 06 апреля 2009

Свойства являются просто установщиками и получателями для ivars и должны (почти) всегда использоваться вместо прямого доступа.

@interface APerson : NSObject {
    // NSString *_name;           //  necessary for legacy runtime
}

@property(readwrite) NSString *name;

@end

@implementation APerson
@synthesize name;                 // use name = _name for legacy runtime
@end

@synthesize создает в этом случае эти два метода (не с точностью до 100%):

- (NSString *)name {
    return [[_name copy] autorelease];
}
- (void)setName:(NSString *)value {
    [value retain];
    [_name release];
    _name = value;
}

Теперь легко отличить ivars от геттеров / сеттеров. Средства доступа имеют префикс self.. В любом случае, вы не должны обращаться к переменным напрямую.


Ваш пример кода не работает должным образом:

_myVar = some_other_object;      // _myVar is the ivar, not myVar.
self.myVar = some_other_object;  // works too, uses the accessors
8 голосов
/ 06 апреля 2009

Синтезированное свойство с именем prop фактически представлено двумя методами prop (возвращая текущее значение свойства) и setProp: (устанавливая новое значение для prop).

Синтаксис self.prop является синтаксическим сахаром для вызова одного из этих методов доступа. В вашем примере вы можете выполнить одно из следующих действий, чтобы установить свойство myVar:

self.myVar = @"foo"; // handles retain/release as specified by your property declaration
[self setMyVar: @"foo"]; // handle retain/release
_myVar = @"Foo"; // does not release old object and does not retain the new object

Чтобы получить доступ к свойствам, используйте self.propname. Для доступа к переменным экземпляра используйте только имя переменной экземпляра.

5 голосов
/ 31 июля 2009

Проблема с использованием стратегии @synthesize myVar = _myVar заключается в том, что я решил, что при написании такого кода, как:

myVar = some_other_object; // doesn't work.

Компилятор жалуется, что myVar не объявлено. Почему это так?

Поскольку переменная myVar не объявлена.

Этот оператор использует синтаксис для доступа к переменной, будь то переменная экземпляра или какой-либо другой вид. Как сказал вам rincewind, для доступа к свойству вы должны использовать либо синтаксис доступа к свойству (self.myVar = someOtherObject), либо явное сообщение методу доступа ([self setMyVar:someOtherObject]).

В противном случае вы пытаетесь получить доступ к переменной, а поскольку у вас нет переменной с именем myVar, вы пытаетесь получить доступ к переменной, которая не существует.

2 голосов
/ 06 апреля 2009

В общем, я называю мои свойства такими же, как мои переменные экземпляра; это предположение по умолчанию, которое используется в синтаксисе @property. Если вы обнаружите, что сражаетесь со значениями по умолчанию, вы делаете это неправильно (или, по моему мнению, ваш системный пакет, что не относится к Cocoa / Cocoa-touch).

Ошибка компилятора, которую вы получаете, потому что использование свойства всегда должно иметь ссылку на объект, даже внутри вашей собственной реализации класса:

self.stuff = @"foo"; // property setter
[stuff release]; // instance variable
stuff = @"bar"; // instance variable
return self.stuff; // property getter

Я знаю, что многие программисты Какао не согласны, но я думаю, что это плохая практика - использовать свойства внутри реализации вашего класса. Я бы предпочел увидеть что-то вроде этого:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    [stuff release];
    stuff = [theStuff copy];
    // do something else
}

чем это:

-(void) someActionWithStuff: (NSString*) theStuff {
    // do something
    self.stuff = theStuff;
    // do something else
}

Я предпочитаю делать управление памятью как можно более подробно. Но даже если вы не согласны, использование формы self.stuff поможет любому опытному программисту Objective-C вызывать свойство, а не обращаться к переменной экземпляра. Это тонкий момент, который новичкам легко замаскировать, но после того, как вы некоторое время поработали с Objective-C 2.0, это довольно ясно.

0 голосов
/ 21 июня 2013

что не так с простым использованием

@interface MyClass : NSObject
@property NSString *prop;
@end

nonatomic и retain не требуются, retain является значением по умолчанию, а atomic / nonatomic не важен, если XCode не сообщит вам предупреждение.

НЕ обязательно объявлять iVar, он будет создан для вас с именем _prop, если вы действительно хотите его использовать (я не понимаю, почему честно)

@ синтез не требуется.

когда (и вы должны) использовать ARC, вам не нужно беспокоиться о сохранении и освобождении.

будь проще!

Более того, если у вас есть такой метод

- (void)aMethod:(NSString*)string
{
    self.prop = string;
    // shows very clearly that we are setting the property of our object

    _aName = string;
    // what is _aName ? the _ is a convention, not a real visual help
}

Я бы всегда использовал свойства, более гибкие, удобные для чтения.

0 голосов
/ 31 июля 2009

Поскольку Apple резервирует префикс _ для себя, и поскольку я предпочитаю делать его более очевидным, когда я использую сеттер и когда я использую ивар, я принял принцип использования префикса i_ на моих иварах, так например:

@interface MyClass : NSObject {
    NSString *i_myVar;
}

@property (nonatomic, retain) NSString *myVar;

@synthesize myVar = i_myVar;

i_myVar = [input retain];
self.myVar = anotherInput;
[i_myVar release]

Поскольку очень важно знать, когда вы используете сеттер и когда вы используете ивар, я считаю, что явно другое имя безопаснее.

В вашем вопросе должно быть:

self.myVar = @"Foo"; // with setter, equivalent to [self setMyVar:@"Foo"]

и

_myVar = some_other_object;  // direct ivar access - no memory management!

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

0 голосов
/ 31 июля 2009

Don

Согласно "правилам", вы должны вызывать Release для каждого Copy, Alloc и Retain. Так почему ты звонишь в Release? Предполагается ли это, что он был создан с использованием Alloc, Copy или Retain?

Это поднимает другой вопрос: вредно ли вызывать Release для ссылки на объект, если он уже был освобожден?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...