Dot notation while subscripting question

For the removing assets challenge, I wanted to select one of the employees and access their assets. For me, since Employee[3] had two assets, I chose to work with this employee. I wanted to see if I could avoid defining a variable for this employee, so I tried the following code to access the assets (I tried with and without the parenthesis):

I got an error immediately saying “Property ‘assets’ not found on object of type ‘id’”. I also noticed that if I don’t use dot notation, as in the following, then it works fine:

Actually, before I noticed the issue above, I tried

This gave the same error.

Is there a discussion in the book or on this forum that discusses why the dot notation I tried above doesn’t work but the ordinary message send does work. I thought I tried similar things in Python before without any problems.

Otherwise, can anybody explain what is wrong with using dot notation in this case?

Thanks

Forget about Python, now you are programming with Objective-C.

The reason the above code fails to compile is this: When you index an array to get an object out, what you get is statically typed as id, not typed as the type of the original object that was put in the array previously.

According to NSArray class reference:

- (id)objectAtIndex:(NSUInteger)index

[code]NSMutableArray * employees = [NSMutableArray array];
Employee * employee = [Employee new];

// Add one employee
[employees addObject];

// Get it back and access a property
NSArray * assets = [employees objectAtIndex:0].assets; // ← error

// Get it back and access a property
assets = employees [0].assets; // ← error
[/code]
Because in both instances, the object returned is statically typed as id (not as Employee), you cannot apply the dot operator to access a property of the object.

However, you can send a message (in fact, any message) to it:

NSArray * assets = [employees [0] assets];

See Also: Programming with Objective-C: developer.apple.com/library/mac … 10-CH1-SW1

ibex10: Thanks for the information.

In the code that works (without the dot), we are still subscripting, so don’t we still return an object of type id. So how does [idObject asset] work still? In other words, how does the idObject have a method asset? You are clearly right, but I guess I don’t quite understand why one way works and one way does not.

Update: I bet I am not understanding the role of statically in your answer. Can you elaborate on what you mean by that here?

[quote]Update: I bet I am not understanding the role of statically in your answer. Can you elaborate on what you mean by that here?
[/quote]
When you have a pointer to some object, the static type information available to the compiler determines what you can do syntactically with that object; for example, can you apply the dot notation to it?

For example, imagine the following scenario (using very simple C++ code):

// details all open...
struct SolarPanel
{
   unsigned long maxPowerRating;
};

const SolarPanel * solarPanel (const char * brandName);

Also imagine that the compiler has seen the above code, before encountering the following code:

const SolarPanel * panel1 = solarPanel ("brand 1");
const SolarPanel * panel2 = solarPanel ("brand 2");

if (panel1->maxPowerRating > panel2->maxPowerRating) {
    // add panel1 to favourites
}

Since the static type information available reveals that a SolarPanel object has a property named maxPowerRating, the if statement above compiles without errors.

Now imagine the following scenario:

// details all hidden...
struct SolarPanel;

const SolarPanel * solarPanel (const char * brandName);

unsigned long maxPowerRating (const SolarPanel *);

Also imagine that the compiler has seen only the above code, before encountering the following code:

const SolarPanel * panel1 = solarPanel ("brand 1");
const SolarPanel * panel2 = solarPanel ("brand 2");

if (panel1->maxPowerRating > panel2->maxPowerRating) {
    // add panel1 to favourites
}

Since the static type information available in this case reveals nothing about a SolarPanel object, the compiler is unable to tell that maxPowerRating is a property of that object and therefore complains about the test expression of the if statement.

If, however, the compiler saw the following code:

const SolarPanel * panel1 = solarPanel ("brand 1");
const SolarPanel * panel2 = solarPanel ("brand 2");

if (maxPowerRating (panel1) > maxPowerRating (panel2)) {
    // add panel1 to favourites
}

It would happily compile that code, even though the compiler still does not know that a SolarPanel object has a property named maxPowerRating. This is because, presumably, the unsigned long maxPowerRating (const SolarPanel *) function knows what a SolarPanel object looks like on the inside.

Yes

That’s because you can send any message to an object statically typed as id; however,if the object does not understand that message, a runtime error is triggered, for which you have to be prepared.

I hope this clears things up a bit for you.

[quote=“ibex10”]

Yes

That’s because you can send any message to an object statically typed as id; however,if the object does not understand that message, a runtime error is triggered, for which you have to be prepared.

I hope this clears things up a bit for you.[/quote]

Ooh. I was afraid that you weren’t kidding when you said you could send any message to an id object. Okay, what if the method you send is a common method to more than one class? How does the compiler know which one you mean? For example, what if some class other than BNREmployee had a method called asset? Is there a way to specify which class’s method to pass?

The compiler generates code for the run-time system to send the message to the specified object. So in the following example, there is no confusion over where the messages are sent even though both Bar and Foo have a method named rotateBy:. There is nothing special about this; the object specified in a message-send expression or statement is always the first recipient of that message.

// Both Bar and Foo have a method named rotateBy:
id anObject = [Bar new];
id anotherObject = [Foo new];

[anObject rotateBy:90]; 
[anotherObject rotateBy:90];

If you want to learn more about this, the following documents are worth reading:
- Objective-C Runtime Reference
- Objective-C Runtime Programming Guide

Thanks. I have not read about the runtime yet. I will look into that.