Назначение свойства Objective-C возвращает назначенное значение?
-
20-09-2019 - |
Вопрос
Скажем, у меня есть следующее:
@interface MyClass : NSObject { NSString* _foobar; }
@property (nonatomic, retain) NSString* foobar;
@end
@implementation MyClass
@dynamic foobar;
- (void) setFoobar:(NSString*)fbSet; { [_foobar release]; _foobar = [fbSet retain]; }
- (NSString*) foobar; { return _foobar; }
@end
Затем:
MyClass* mcInst = [[[MyClass alloc] init] autorelease];
NSLog(@"I set 'foobar' to '%@'", (mcInst.foobar = @"BAZ!"));
Глядя на возвращаемое значение -[MyClass setFoobar:]
, можно предположить, что эта строка напечатает I set 'foobar' to ''
, поскольку назначение ничего не возвращает.
Однако, к счастью, это присвоение действует так, как ожидалось, и код печатает I set 'foobar' to 'BAZ!'
.К сожалению, это похоже на противоречие, поскольку возвращаемое значение вызванного установщика противоречит тому факту, что присваивание возвращает присвоенное ему значение.Сначала я так понял mcInst.foobar = @"BAZ!";
вместо блока делает два вызова:сначала установщик, а затем геттер для сбора возвращаемого значения.Однако оснащение методов установки и получения с помощью NSLog
звонки доказывают, что это не так.
Решение
Краткое резюме:
Быстрый ответ здесь: противоречия нет, потому что результат выражения:
(mcInst.foobar = @"BAZ!")
на самом деле @"BAZ!"
, и нет mcInst.foobar
.
Более подробная информация доступна ниже, но может быть полезно рассмотреть следующую модификацию вашего setFoobar
метод:
- (void) setFoobar:(NSString*)fbSet
{
[_foobar release];
_foobar = [[NSString stringWithFormat:@"HELLO_%@", fbSet] retain];
}
При наличии этого кода значение foobar
свойство изменяется во время его установки, но ваша строка кода по-прежнему будет отображать значение «БАЗ!».
Подробности:
Как указывает новыйаккт, ваш код NSLog работает, потому что вы используете оператор присваивания (=), который имеет очень специфическое поведение в языке C (на котором основан Objective-C).
В C вы можете сделать следующее:
x = y = z = 42;
и все переменные, x
, y
и z
будет содержать значение 42.
Компилятор обрабатывает это поведение, используя временную переменную (*).По сути, то, что происходит за кулисами, выглядит примерно так:
tempVar = 42;
z = tempVar;
y = tempVar;
x = tempVar;
В том же духе вы можете сделать следующее:
SomeFunction(x = 42);
эта строка кода скопирует значение 42 в x, а затем вызовет SomeFunction
с аргументом 42.За кадром это выглядит так:
tempVar = 42;
x = tempVar;
SomeFunction(tempVar);
Теперь в Objective-C ваша строка журнала обрабатывается следующим образом:
tempVar = @"BAZ!";
[mcInst setFooBar:tempVar];
NSLog(@"I set 'foobar' to '%@'", tempVar);
(*) обратите внимание, что использование «переменной temporaray», которую я описываю, предназначено для иллюстрации концепции и может фактически не отражать то, что на самом деле делает тот или иной компилятор «под капотом».Детали реализации такого рода зависят от программистов, которые пишут компилятор, и каждый из них может делать что-то свое.Конечный результат, однако, тот же.
Другие советы
Нет необходимости вызывать геттер — ему присваивается значение прямо в той же строке.Вы можете думать об этом как о расширении до [mcInst setFoobar:@"BAZ!"], @"BAZ!"
.
в C присваивание — это выражение, результатом которого является присвоенное значение.
Это связано с тем, как работает оператор присваивания C.Как описано в стандарте ANSI C:
«Оператор назначения хранит значение в объекте, обозначенном левым операндом.Выражение назначения имеет значение левого операнда после назначения ... "
Ваше выражение присваивания mcInst.foobar = @"BAZ!"
.Мне кажется, имеет смысл, что, хотя назначение работает путем вызова метода mcInst, поведение такое же, как и в C.Значением выражения присваивания является левый операнд после присваивания (@"BAZ!"
), поэтому это значение передается функции NSLog.
Это то же самое поведение, которое позволяет вам написать инициализатор в стиле if (self = [super init])
.
P.S.Справедливо спросить, почему компилятор должен вызывать метод установки свойства при присвоении ему значения, а не вызывать метод получения при использовании значения mcInst.foobar
после.Я бы сказал, что просто предполагается, что геттер вернет то же значение, которое только что было присвоено свойству, и поэтому геттер не вызывается.