views:

181

answers:

5

I was working on a program today and hit this strange bug. I had a UIButton with an action assigned. The action was something like:

-(void) someaction:(id) e
{
    if ([e tag]==SOMETAG)
    {
        //dostuff
    }
}

What confuses me is that when I first wrote it, the if line was

if (e.tag==SOMETAG)

XCode refused to compile it, saying

error: request for member 'tag' in 'e', which is of non-class type 'objc_object*'

but I thought the two were equivalent.

So under what circumstances are they not the same?

A: 

You can't use dot notation on id objects. It's just a rule they have.

Mike Howard
+5  A: 

Using the dot notation is only possible if the variable has an associated property declared, or if there are Key-Value-Coding compliant accessor methods available. The property syntax allows you to 'synthesise' accessor methods for your variable that are Key-Value-Coding compliant, and really, this is how the dot notation works.

When a property is declared, someObject.variable is equivalent to [someObject variable].

When an object is typed as id, the compiler isn't aware of any properties the object has. id is a pointer to any object, effectively a void*.

You could cast your object to the type that you expect it to be, which would allow you to then use the property syntax.

((MyObject*)e).tag
Jasarien
Didn't know that. It makes sense though. Thanks for being helpful.
Jergason
This is wrong. You can use dot notation with any *declared* accessor method whether declared as a property or as a method/pair of methods.
JeremyP
Yes, you are correct. I will update my answer.
Jasarien
`((MyObject*)e).tag`.
KennyTM
Yes Kenny, you are correct. My original version was casting the return value from the tag property rather than casting the `e` object. Thanks.
Jasarien
A: 

This is for Jack who deleted his answer before I could comment :-)

You're not accessing the instance variable directly, the dot notation is only used for getting at properties, via the getter and setter methods that you @synthesized. You're sending a message to the object, which in this case causes those getter/setter methods to get called. You can't use the dot notation to access a member variable directly, or call a method directly - it has to be set up as a property.

Mike Howard
+1  A: 

You can use the obj.prop getter syntax provided (handwavily) that the compiler has a prototype for the method call [obj prop], and the setter syntax provided there's [obj setProp:blah]. This means you can use it with classes like NSNumberFormatter which don't declare properties. Whether this is advisable or not is up for debate.

You can cast it to the right object type with ((Foo*)obj).prop, if you don't mind all the parentheses.

Note that the return type of an unknown method defaults to id (so you might be comparing an id to an int), and also that your sender might not be a UIView (it could be a UIBarButtonItem). It's a bit safer to do if ([(id<NSObject>)e isKindOfClass:[UIView class]] && [(UIView*)e tag] == sometag), if you assume that e conforms to <NSObject>.

tc.
+2  A: 

There seems to be some misconception about properties and dot notation going on here. Dot notation can be used with any accessor method whether declared as a property or not, provided that the compiler knows that the object to the left of the dot has the accessor. You do not need to use the @property syntax. You do not need to synthesize the accessor. The following is perfectly valid Objective-C which will compile cleanly:

#import <Cocoa/Cocoa.h>
@interface AClass
{
}

-(NSString*) aProperty;

@end

@implementation AClass

-(NSString*) aProperty
{
    return @"some text";
}

@end

int main()
{
    AClass* foo = [[ACLass alloc] init];
    NSLog(@"%@", foo.aProperty);
    return 0;
}

The critical point is that the compiler has to know if the accessor exists for the object which means that the object must be correctly typed (i.e. can't be id).

JeremyP
Why wasn't dot notation restricted to @property-declared accessors? It seems like it would have resolved ambiguous situations (e.g., `.uppercaseString`) and allowed the class author to make the compiler enforce their intent. Is there a technical or philosophical reason?
Preston
@Preston: I'm not sure what you mean about ambiguous situations. There doesn't seem to me to be any ambiguity about what `uppercaseString` *should* do.
JeremyP
Being able to call `-uppercaseString` using dot notation implies that it's a property.
Preston
@Preston: No. It's the name of the selector that clues you in, not anything else, nor can it be. Even if dot notation were restricted to selctors declared with `@property`, I could still create a "property" called, say, saveFileToDisk or some other action.
JeremyP
In that example, as the class author, it would be your intent that there is a poorly-named property called `safeFileToDisk` and that it be callable in dot notation. Right now, the compiler makes no guarantee that what you're calling in dot notation is a property at all.
Preston
@Preston: precisely. Making it so that you cannot use dot notation on accessors not declared with @property cannot help that situation.
JeremyP
Restricting it would help that situation because the compiler would be enforcing the intent of the class author, so even if it was a poorly named property, you'd know it was a property. It would also appear in the IDE's auto-completion list when using dot-notation. The issue here is that even though dot-notation is for calling accessors according to Apple, there is no sanity check by the compiler, so `anObject.retain` will compile and run. @property declarations conveniently describe which class's methods are accessors and which are not, so I don't understand why this information isn't used.
Preston
@Preston: No it would not restrict the situation because the compiler cannot divine the intent of the author. Knowing that something is a property doesn't help because properties are just accessors. How can the compiler tell that my `@property ... sendFileToDisk` is wrong?
JeremyP
I don't think I'm following you. I'm saying that the compiler could differentiate between behavioral methods and accessors by restricting dot-notation to `@property` accessors. If the compiler enforced things in the way I'm describing, you'd know that a poorly-named property like `sendFileToDisk` was still a genuine property, and you could even get a warning if you tried to call it as a behavioral method via `[anObject sendFileToDisk]`. Even though it's poorly named, the compiler could still enforce the class author's intent that it's a property when clients of the class use it.
Preston