DevilInDisguise
DevilInDisguise

Reputation: 360

objective c operator precedence

If theres one thing I hate, its going by something without completely understanding the logic behind it, so.... I stumbled upon this expression :

 MSClient *client = [(AppDelegate *) [[UIApplication sharedApplication] delegate] client];

I have actually always writing access to a variable with the dot-syntax, and used hard brackets solely for method calls, but I guess this is just whatever one like. My confusion lies within that I would normally write it as following:

 MSClient *client = ((AppDelegate *) [[UIApplication sharedApplication] delegate]).client;

since this will not work:

     MSClient *client = (AppDelegate *) [[UIApplication sharedApplication] delegate].client;

due to the cast not being applied to the right object, I wonder how the first written expression compiles smoothly without any errors, since it seems to me the cast is not bracketed in and such applied to "delegate"; it applies the outermost hard brackets, remove the "." AND remove the brackets that make 3rd expression work compared to 2nd which don't.

Phew, sorry for a long post about something as simple. But again, I hate going by something without full understanding.

Thanks!

Upvotes: 0

Views: 327

Answers (2)

Ken Thomases
Ken Thomases

Reputation: 90531

It doesn't seem that the accepted answer actually addressed the question. You asked why this:

MSClient *client = [(AppDelegate *) [[UIApplication sharedApplication] delegate] client];

compiles "since it seems to me the cast is not bracketed in and such applied to "delegate"".

In that expression, the cast can only apply to the result of [[UIApplication sharedApplication] delegate]. That's the only complete subexpression that it precedes. It can't apply to the result of the -client message-send expression, because that message-send expression begins with the first left bracket and the cast is inside that.

Here, I illustrate breaking the expression up:

[v----------------subexpression for receiver----------------v client]
 (AppDelegate *) [v---subexpression for receiver--v delegate]
                  [UIApplication sharedApplication]

The syntax for a cast is that it comes before a subexpression and modifies that subexpression's type. In this case, it modifies the middle of the above lines. It can't modify the first because to do so it would have to precede it — come before the first left bracket — and it doesn't. It can't modify the third because the second left bracket, which begins the middle subexpression, is between the cast and the third subexpression. The cast doesn't immediately precede the third subexpression.

Upvotes: 1

Amin Negm-Awad
Amin Negm-Awad

Reputation: 16650

This is because of the operator precedence in C (or Objective-C, in this case):

In

(AppDelegate *) [[UIApplication sharedApplication] delegate].client

the dot operator . binds stronger than the cast operator (AppDelegate*). First the client is fetched and than it is casted. Therefore it is equivalent to

(AppDelegate *)([[UIApplication sharedApplication] delegate].client)

In my own words:

  • The return type of client (MSClient instance reference) is casted to an AppDelegate instance reference.
  • The return type of delegate (NSObject <NSApplicationDelegate>) is not casted to anything.

The working approach changes the subject of the cast:

((AppDelegate *)[[UIApplication sharedApplication] delegate]).client;

Using the parentheses you explicitly say that the return value of delegate is subject of the cast.

One might think that a different precedence of the dot operator solves that. But this will lead to ugly could in other situations. Using precedence for code readability is fragile.

I do not think that this is really a problem. If it is for you, you can simply add an additional method -appDelegate to UIApplication that has the correct typing like that:

@interface UIApplication (MyAddition)
- (AppDelegate*)appDelegate;
+ (AppDelegate*)appDelegate;
@end

@implementation UIApplication (MyAddition)
- (AppDelegate*)appDelegate
{
  return (AppDelegate*)self.delegate;
}

+ (AppDelegate*)appDelegate
{
  return (AppDelegate*)[[self sharedApplication] delegate];
}

It is a one-timer per project.

You can use it as so:

… [UIApplication sharedApplication].appDelegate.client … // -appDelegate
… [UIApplication appDelegate].client …                   // +appDelegate

Typped in Safari.

Upvotes: 1

Related Questions