6

Скажем, у меня есть следующие:Назначение объекта Objective-C возвращает присвоенное значение?

@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!"; делает два вызова вместо блока: сначала сеттер, а затем геттер, чтобы собрать возвращаемое значение. Однако использование инструментов setter и getter с помощью NSLog звонков доказывает, что это не так.

+1

Я думаю, а не «Почему NSLog работает?», Лучший вопрос (и я думаю, что вы задаете вопрос): «Почему сеттер возвращается в пустоту, а не (в данном случае) NSString *? предположил, что mcInst.foobar = был просто синтаксическим сахаром для [mcInst setFoobar: ...] Я думаю, не ... –

+1

Да, вопрос в том, почему компилятор выдает ошибку, когда вы используете 'NSLog (@" I set ' foobar 'to'% @ '", [mcInst setFoobar: @" BAZ! "]),' признавая, что метод ничего не возвращает, но позволяет сделать это другим способом. – Dimitris

+0

Брайан: да, это было на самом деле вопрос, который я пытался задать, хотя моя формулировка, возможно, скрыла это =) – rpj

ответ

9

Быстрый Резюме:

Быстрый ответ здесь является то, что нет никакого противоречия, потому что результат выражения:

(mcInst.foobar = @"BAZ!") 

@"BAZ!" на самом деле, и не mcInst.foobar.

Более подробно можно ниже, но это может помочь рассмотреть следующую модификацию Вашего setFoobar метода:

- (void) setFoobar:(NSString*)fbSet 
{ 
    [_foobar release]; 
    _foobar = [[NSString stringWithFormat:@"HELLO_%@", fbSet] retain]; 
} 

С помощью этого кода в месте, значение foobar имущества изменяется, пока она установлена , , но ваша строка кода по-прежнему будет отображать значение «BAZ!».

Детали:

Как отметил newacct, код 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 в х, а затем вызвать SomeFunction с аргументом 42. за кулисами, это выглядит следующим образом:

tempVar = 42; 
x = tempVar; 
SomeFunction(tempVar); 

Теперь, в Objective-C, ваша регистрация линия обрабатывается следующим образом:

tempVar = @"BAZ!"; 
[mcInst setFooBar:tempVar]; 
NSLog(@"I set 'foobar' to '%@'", tempVar); 

(*) обратите внимание, что использование в «temporaray переменной» я описываю предназначен для иллюстрации концепции, а не на самом деле может отражать то, что любой данный компилятор на самом деле делает под капотом. Этот вид детализации реализации зависит от программистов, которые пишут компилятор, и каждый может сделать что-то другое. Конечный результат, однако, тот же.

+0

Я понимаю поведение C и то, как это выполняется компилятором. Я полагаю, что ваша точка компилятора, использующая временную переменную, имеет смысл, так что спасибо. Однако основное внимание в моем вопросе было на кажущемся противоречии между методом, возвращающим void (который является синтаксисом свойства в Objective-C) и присваиванием, возвращающим присвоенное значение. – rpj

+1

Хм. Я думаю, что я, возможно, похоронил ключевую концепцию под слишком подробными подробностями. Если вы посмотрите на последний блок кода в моем ответе, вы можете понять, почему использование временной переменной делает возвращаемое значение атрибута свойства неактуальным. Аксессор свойств просто вызывается как один из шагов при выполнении строки 'NSLog (...)', и это временная переменная, которая передается в 'NSLog'. –

+0

Я поставил критический пункт в верхней части моего ответа и добавил еще один пример, который может помочь проиллюстрировать. Я надеюсь, что это помогает! –

0

в C, присваивание является выражение, которое вычисляется в присвоенное значение

+0

Я понимаю, и даже указал на это. Я ищу детали реализации Objective-C. – rpj

0

Это связано с тем, как работает оператор назначения C. Как описано в стандарте ANSI C:

«Оператор присваивания сохраняет значение в объекте, назначенному левый операнд выражения присваивания имеет значение левого операнда после выполнения задания ...»

Ваше выражение назначения - mcInst.foobar = @"BAZ!". Мне кажется, что даже если назначение работает, вызывая метод по mcInst поведению так же, как и C. Значение выражения присваивания - это левый операнд после назначения (@"BAZ!"), поэтому это значение передается в Функция NSLog.

Это то же поведение, что позволяет вам написать инициализатор в стиле if (self = [super init]).

P.S. Является справедливым вопросом, чтобы спросить, почему компилятор должен вызвать установщика в свойстве при назначении ему значения и не вызывать получателя при использовании значения mcInst.foobar впоследствии. Я бы сказал, что просто предполагается, что getter вернет то же значение, которое было только что присвоено свойству, и поэтому геттер не вызывается.

1

Не нужно вызывать геттер - он имеет значение, назначаемое прямо там же. Вы можете думать о нем как о расширении до [mcInst setFoobar:@"BAZ!"], @"BAZ!".