Назначение свойства Objective-C возвращает назначенное значение?

StackOverflow https://stackoverflow.com/questions/2060236

Вопрос

Скажем, у меня есть следующее:

@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 после.Я бы сказал, что просто предполагается, что геттер вернет то же значение, которое только что было присвоено свойству, и поэтому геттер не вызывается.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top