Question about an error when using id


#1

Hi,

Isn’t rectangle.width same as [rectangle width]?

In the following code when I use [color=#BF0000]rectangle.width[/color] I get this error: Property ‘width’ not found on object of type '__strong id’
but it works fine when I use [color=#BF0000][rectangle width][/color]. Why so?

Here are the two examples:

//FIRST EXAMPLE
@autoreleasepool
{
id rectangle = [[Rectangle alloc] init];
[rectangle setWidth:5];
NSLog(@“Rectangle width = %i”, [color=#BF0000]rectangle.width[/color]);
//at the above line I get the error Property ‘width’ not found on object of type ‘__strong id’
}

//SECOND EXAMPLE
@autoreleasepool
{
id rectangle = [[Rectangle alloc] init];
[rectangle setWidth:5];
NSLog(@“Rectangle width = %i”, [color=#BF0000][rectangle width][/color]);
}

Thanks!


#2

Did you declare the width as a property in the class of which the rectangle is an instance?

To use the dot syntax on objects that are instances of a class, you need to declare properties, which are covered near the end of the book.

@interface Rectangle: NSObject

@property float width;
@property float height;

@end

#3

Thanks for your response. This is what I did:

@interface Rectangle : Shape
{
int width;
int height;
}
@property int width;
@property int height;

@end


#4

You don’t have to declare the instance variables explicitly; they will be automatically synthesised if you don’t declare them.

@interface Rectangle : Shape
@property int width;
@property int height;
@end

@implementation Rectangle

@synthesize with = _wideness;  // optional: specify a different name for the instance variable
@synthesize height;
...
@end 

#5

Thanks for the info. about instance variables, I am still trying to fully understand how they are different from properties.

I tried removing them and but got the same error. I don’t get any error if I change the id in the following line to Rectangle:
id rectangle = [[Rectangle alloc] init];


#6

Avoid using [color=#FF0000]id[/color] whenever you can, because [color=#FF0000]id[/color] contains no static type information.

Look at it this way: Instance variables hold the actual data. You can directly read the data or modify it, or you can do it indirectly through its corresponding property if there is one. Properties enable you to access the data indirectly, indirectly because every time you use a property to access the data, a method invocation takes place behind the scenes (also if the data is modified, a notification message may be sent out):

For example (copy and paste into Xcode to view:

//  main.m

#import <Foundation/Foundation.h>

#define MY_Log() NSLog (@"%s", __PRETTY_FUNCTION__)
#define MY_Log1(P1, P2) NSLog (@"%s: "P1, __PRETTY_FUNCTION__, P2)
#define MY_Logs(P1) NSLog (@"%s: %s", __PRETTY_FUNCTION__, P1)

@interface Foo: NSObject
@property int fooness;

- (void)change;

@end

@interface Bar: NSObject
- (void)startMonitoringFoo:(Foo *)foo;
- (void)stopMonitoringFoo:(Foo *)foo;
@end

int main (int argc, const char * argv[])
{
    @autoreleasepool
    {
        Bar *bar = [[Bar alloc] init];
        Foo *foo = [[Foo alloc] init];
        
        MY_Logs ("bar start monitoring foo...");
        [bar startMonitoringFoo:foo];
        
        MY_Logs ("change foo indirectly...");
        foo.fooness = -1;
        foo.fooness = 0;
        foo.fooness = 1;
        
        MY_Logs ("change foo directly...");
        [foo change];

        MY_Logs ("bar stop monitoring foo...");
        [bar stopMonitoringFoo:foo];
        
        // let run time go to steady state
        [NSThread sleepForTimeInterval:5.0];
    }
    MY_Logs("Done!");
    return 0;
}

@implementation Foo

@synthesize fooness = degreeOfFooness;

- (void)change
{
    int v = 100;
    while (v--)
    {
        degreeOfFooness = v;  // changing the intance variable directly
    }
}

@end

@implementation Bar

- (void)startMonitoringFoo:(Foo *)foo
{
    [foo addObserver:self forKeyPath:@"fooness" options:nilHandleErr context:nil];
}

- (void)stopMonitoringFoo:(Foo *)foo
{
    [foo removeObserver:self forKeyPath:@"fooness"];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    MY_Log1("%@", keyPath);
    MY_Log1("%@", change);
}

@end

Its output:

2012-09-27 16:26:44.489 PrivateVariables[8879:403] int main(int, const char **): bar start monitoring foo...
2012-09-27 16:26:44.492 PrivateVariables[8879:403] int main(int, const char **): change foo indirectly...
2012-09-27 16:26:44.492 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: fooness
2012-09-27 16:26:44.495 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: {
    kind = 1;
    new = "-1";
    old = 0;
}
2012-09-27 16:26:44.495 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: fooness
2012-09-27 16:26:44.496 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: {
    kind = 1;
    new = 0;
    old = "-1";
}
2012-09-27 16:26:44.496 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: fooness
2012-09-27 16:26:44.497 PrivateVariables[8879:403] -[Bar observeValueForKeyPath:ofObject:change:context:]: {
    kind = 1;
    new = 1;
    old = 0;
}
2012-09-27 16:26:44.497 PrivateVariables[8879:403] int main(int, const char **): change foo directly...
2012-09-27 16:26:44.498 PrivateVariables[8879:403] int main(int, const char **): bar stop monitoring foo...
2012-09-27 16:26:49.499 PrivateVariables[8879:403] int main(int, const char **): Done!

The above example demonstrates the occurrences of method invocations whenever a property is modified. Every time fooness is modified, bar receives notifications. If the corresponding instance variable degreeOfFooness is modified directly, bar will not know about it.


#7

Very cool! Thanks for the example and explanation. This helps me understand the use and purpose of properties.