Why can dot syntax not be used on a method whose return type is instancetype?
Asked Answered
S

3

11

Pretend I have a category on NSObject that defines the following method:

+ (instancetype)allocTemplate
{
    id instance = [self new];

    return instance;
}

and I have the following class:

@interface FDActor : NSObject

@property (nonatomic, copy) NSString *name;

+ (void)sayHi;    

@end


@implementation FDActor

+ (void)sayHi
{
    [self allocTemplate].name;
}

@end

How come [self allocTemplate].name errors out at compile time if self is FDActor?

I am aware it works if you use normal message sending syntax but I am explicitly interested in the dot syntax error.

Syncopate answered 4/4, 2014 at 14:51 Comment(9)
What's the error? I tried something similar and mine compiled.Benzofuran
Property 'name' not found on object of type 'id'. Just to be clear I don't mean replace "self" with "FDActor". I mean call that method inside a class method inside FDActor.Syncopate
If the category is on NSObject, wouldn't instancetype return something typed as NSObject?Boleslaw
I've always used . syntax to access ivar members only, and messages [] to access properties. I know the compiler sometimes can fudge it but one tends to confuse one's self.Aspirin
@michaels I defined + (instancetype)allocTemplate on FDActor as well and got the same result.Syncopate
Is the name property public?Aga
@RobP, using dot-syntax to access properties does not require any "fudging" on the compiler's part. It's an explicitly supported, well-defined feature of Objective-C (2.0). On the other hand, dot-syntax is not used for accessing ivars. You either use the ivar's name directly, or dereference the self pointer using self->_ivar (the compiler does this for you if you use the ivar name only). Whether or not to use dot syntax for properties is a matter of code style/personal taste. The OP's question is really only related to how the compiler interprets methods returning an instancetype.Soares
good points, I was just suggestions a code convention I use for myself. I wonder what [[self class] allocTemplate].name would do in the above question...Aspirin
I found this article educational, too: nshipster.com/instancetypeAspirin
R
3

It would appear as though instancetype is used only for type checking during assignment, so FDActor *actor = [FDActor allocTemplate] would not produce a warning.

If we cast the return type of allocTemplate the problem goes away.

- (void)sayHi { ((__typeof__(self))[[self class] allocTemplate]).name; }

But note that this only works in an instance method since the typeof an instance is another instance. Note also that since we now explicitly type the return value the allocTemplate method is no longer necessary, if all were looking for is type checking then we can even just cast nil and it will work.

If we try the same thing in a class method it doesn't work

+ (void)sayHi { ((__typeof__(self) *)[self allocTemplate]).name; } This is because (__typeof__(self) *) doers not evaluate to FDActor * but Class * which ARC will complain about. It looks like there is no way to resolve the type information FDActor * in a generic way from within a class method at compile time.

I rather expected the instancetype keyword to be a little more useful.

Rubstone answered 4/4, 2014 at 17:41 Comment(0)
A
1

The error message tells you. [self allocTemplate] is an id. Property syntax never works on id.

Note that this does work:

[FDActor allocTemplate].name;

I think the class to which instancetype is to be related must be specified in the call; otherwise we just fall back on id.

Adaptation answered 4/4, 2014 at 15:31 Comment(3)
I understand this but what is the point of instancetype then?Syncopate
Sorry I didn't mean that I don't know what instancetype is. I mean what is the point of instancetype if it is treated as an id for all methods or dot syntax properties called on it. The whole point of the instancetype is that it is recognized as a type of whatever class called the method. I want type checking on method calls not just assignments.Syncopate
I think I've got it working (and that the way I've done it shows what the problem is).Adaptation
S
0

I'll take a stab at this, with the disclaimer that I don't fully understand how instancetype support is actually implemented, let alone all the nitty gritty details of the compiler's type checking system. In other words, this is my best guess, and not an authoritative or complete answer...

After compilation, Objective-C methods are actually functions, where self is the first argument to the function, and is typed id. So you have:

void sayHi(id self, SEL _cmd)
{
     [self allocTemplate].name; // Actually calls to objc_msgSend, but that doesn't matter here
}

The compiler's treatment of the type of value returned by +allocTemplate here seems to be related to this. If you change self to an explicit FDActor the error goes away. Of course, for methods -- as opposed to properties -- the compiler types self as you'd expect, and will warn (or error under ARC) for methods that self doesn't appear to respond to. It seems like this is perhaps a difference (bug?) in the compiler's checking for available methods vs. properties in the context of instancetype.

Soares answered 4/4, 2014 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.