Причина использования ivars против свойств в объекте c
Мне не удалось найти какую-либо информацию по этой теме, и большинство из того, что я знаю об этом, произошло в результате полной аварии (и несколько часов, пытаясь понять, почему мой код не работает). Изучая objective-c большинство учебных пособий, которые я нашел, производят переменные и свойства с тем же именем. Я не понимаю значение, потому что кажется, что свойство делает всю работу, а переменная просто видна там. Например:
test.h
@interface Test : NSObject {
int _timesPlayed, _highscore;
}
@property int timesPlayed, highscore;
// Methods and stuff
@end
Test.m
@implementation Test
@synthesize timesPlayed = _timesPlayed;
@synthesize highscore = _highscore;
// methods and stuff
@end
Что я знаю
1) Итак, сегодня я узнал (после нескольких часов смущения), что независимо от того, насколько сильно вы меняете свойства highscore = 5091231
, это ничего не изменит, когда вы попытаетесь вызвать [test highscore], поскольку он все равно будет возвращайте значение _highscore, которое (я думаю) является ivar, которое было установлено в test.h. Таким образом, все изменения переменных в test.m должны меняться _highscore, а не highscore. (Исправьте меня, если я ошибаюсь здесь, пожалуйста)
2) Если я правильно понимаю (я, вероятно, не знаю), то ивары, установленные в test.h, представляют собой фактическую память, где, поскольку @properties - это просто способы доступа к этой памяти. Таким образом, вне реализации я не могу получить доступ к _highscore, не пройдя через свойство.
Что я не понимаю
В основном, что я не понимаю об этой ситуации, нужно ли мне вообще использовать ivars или я могу просто использовать @property и @synthesize. Похоже, что ивары - это просто дополнительный код, который на самом деле ничего не делает, но меня смущает. Некоторые из последних явлений, которые я видел, похоже, не используют иваров, но потом некоторые делают. Так что это всего лишь вещь предпочтения в кодировании или это действительно важно? Я попытался выполнить поиск в Apple Documentation, но я немного потерялся там и, похоже, не нашел то, что искал. Любые рекомендации будут очень благодарны.
Ответы
Ответ 1
Вы можете представить синтаксис для синтеза свойств как @synthesize propertyName = variableName
.
Это означает, что если вы напишете @synthesize highscore = _highscore;
, для вас будет создан новый ivar с именем _highscore
. Поэтому, если вы хотите, чтобы вы могли получить доступ к переменной, которую свойство хранит непосредственно, перейдя к переменной _highscore
.
Некоторый фон
До какой-либо версии компилятора, который я не помню, инструкция синтеза не создавала ivar. Вместо этого он только сказал, какую переменную он должен использовать, поэтому вам нужно было объявить как переменную, так и свойство. Если вы синтезировали префикс подчёркивания, то ваша переменная должна иметь один и тот же префикс. Теперь вам больше не нужно создавать переменную, вместо этого будет создана переменная с variableName
, которую вы указали в инструкции синтеза (если вы еще не объявили ее сами, в этом случае она просто используется как поддерживающая переменная свойства).
Что делает ваш код
Вы явно создаете один ivar под названием highscore
при объявлении переменной и затем неявно создаете другой ivar, называемый _highscore
при синтезе свойства. Это не одна и та же переменная, поэтому изменение одного из них ничего не меняет в отношении другого.
Должны ли вы использовать переменные или нет?
Это действительно вопрос о предпочтении.
Pro переменные
Некоторые люди считают, что код становится более чистым, если вам не нужно писать self.
повсюду. Люди также говорят, что это быстрее, поскольку для этого не требуется вызов метода (хотя он, вероятно, никогда не будет оказывать заметного влияния на производительность ваших приложений).
Свойства Pro
Изменение значения свойства вызовет все необходимые методы KVO, чтобы другие классы могли получать уведомления при изменении значения. По умолчанию доступ к свойствам также является атомарным (к нему нельзя получить доступ из более чем одного потока), поэтому свойство безопаснее читать и записывать из нескольких потоков (это не означает, что объект, на который указывает свойство, является потокобезопасным, если это измененный массив, тогда несколько потоков могут по-прежнему нарушать вещи очень плохо, это только предотвратит, чтобы два потока устанавливали свойство для разных вещей).
Ответ 2
Вы можете просто использовать @property
и @synthesize
без объявления ivars, как вы предложили. Проблема выше в том, что ваш @synthesize
сопоставил имя свойства с новым ivar, созданным компилятором. Итак, для всех целей и задач определение вашего класса теперь:
@interface Test : NSObject {
int timesPlayed;
int highscore;
int _timesPlayed;
int _highscore;
}
...
@end
Назначение значения непосредственно для ivar timesPlayed
никогда не будет отображаться, если вы получите доступ к нему через self.timesPlayed
, так как вы не изменили правильный ivar.
У вас есть несколько вариантов:
1 Удалите два ivars, которые вы указали в своем исходном сообщении, и просто пусть динамический дуэт @property
/@synthesize
сделает свою работу.
2 Измените два ивуара на префикс с помощью подчеркивания '_'
3 Измените свои операторы @synthesize
:
@implemenation Test
@synthesize timesPlayed;
@synthesize highscore;
...
Ответ 3
Обычно я просто использую @property и @synthenize.
@property дает компилятору и указаниям пользователя о том, как использовать ваше свойство. погода у него есть сеттер, что это за сеттер. Какой тип ценности он ожидает и возвращает. Эти инструкции затем используются автозаполнением (и, в конечном счете, кодом, который будет скомпилирован против класса) и @synthesize
@synthesize будет по умолчанию создавать переменную экземпляра с тем же именем, что и ваше свойство (это может запутать)
Я обычно делаю следующее
@synthesize propertyItem = _propertyItem;
это по умолчанию создаст геттер и сеттер и обработает авторекламу, а также создаст переменную экземпляра. Используемая им переменная _propertyItem. если вы хотите получить доступ к переменной экземпляра, вы можете использовать ее как таковой.
_propertyItem = @"Blah";
Это ошибка. Вы всегда должны использовать геттер и сеттер. это позволит освободить приложение и обновить его по мере необходимости.
self.propertyItem = @"Blah";
Это лучший способ справиться с этим. И причина использования секции = _propertyItem для синтезации заключается в том, что вы не можете сделать следующее.
propertyItem = @"Blah"; // this will not work.
он порекомендует вам заменить его на _propertyItem. но вместо этого вы должны использовать self.propertyItem.
Я надеюсь, что эта информация поможет.
Ответ 4
В вашем примере @synthesize timesPlayed = _timesPlayed;
создает новый ivar, называемый _timesPlayed
, и свойство относится к этому ivar. timesPlayed
будет полностью отдельной переменной, не имеющей никакого отношения к свойству. Если вы просто используете @synthesize timesPlayed;
, то свойство будет ссылаться на timesPlayed
.
Целью подчёркивания подчеркивания является упрощение того, чтобы избежать случайного назначения непосредственно ivar, когда вы хотите сделать это через свойство (т.е. методом синтезированного сеттера). Тем не менее, вы все равно можете использовать _timesPlayed напрямую, если хотите. Синтез свойства просто автоматически генерирует геттер и сеттер для ivar.
В общем, вам не нужно объявлять ivar для свойства, хотя могут быть особые случаи, когда вы захотите.
Ответ 5
Это может быть старый вопрос.. но в "современные времена" @synthesize
- НЕ необходимо.
@interface SomeClass : NSObject
@property NSString * autoIvar;
@end
@implementation SomeClass
- (id) init { return self = super.init ? _autoIvar = @"YAY!", self : nil; }
@end
Подпрограмма ivan IS _underscored
синтезируется автоматически... и доступна в реализации этого класса непосредственно (т.е. без вызова self
/вызова автоматически созданных аксессуаров).
Вам нужно всего лишь синтезировать его, если вы хотите поддерживать способность подкласса к доступу к _backingIvar
(без вызова self
) или по множеству других причин, описанных в другом месте.