Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need to cast self to id?

I have an init method that takes an (id) argument:


    -(id) initWithObject:(id) obj;

I'm trying to call it like this:


    [[MyClass alloc] initWithObject:self];

But XCode is complaining about the argument being a "distinct Objective-C type" (which usually indicates a type mismatch or level of indirection error).

If I explicitly cast self to (id) the warning goes away. In either case the code runs as expected. Interestingly, on the next line I'm passing self to another method that also takes an id, and that works fine.

I'm wondering if I'm missing something subtle - or is it a peculiarity of the compiler?

I'm not entirely comfortable just casting it until I'm sure of the reasons why it's necessary.

[Edit]

I've been asked to supply more code. Not sure there's much else that's relevant. Here's my actual code that makes the call. Note that it is, itself, within an init method. It's the call to initWithSource that's giving the warning:


-(id) initWithFrame:(CGRect) frame
{
    self = [super initWithFrame: frame];
    if( self )
    {
        delegate = nil;
        touchDelegate = [[TBCTouchDelegate alloc] initWithSource:self];
        [touchDelegate.viewWasTappedEvent addTarget: self action:@selector(viewWasTapped:)];
    }
    return self;
}

And here's the init method being called:


-(id) initWithSource:(id) sourceObject
{
    self = [super init];
    if (self != nil) 
    {
        // Uninteresting initialisation snipped
    }
    return self;
}
like image 556
philsquared Avatar asked Nov 23 '08 15:11

philsquared


1 Answers

Typically this means there's multiple initWithSource: method names on different classes with conflicting argument types. Remember, if a variable is typed as id the compiler does not know what class it is. Thus if you call initWithSource: on an id-typed object and multiple classes have an initWithSource: method, the compiler essentially just picks one of the two. If it picks the "wrong" one, well, you get a "distinct Objective-C type" error.

So why is this happening to you? I'm not 100% sure, but remember that +[TBCTouchDelegate alloc] returns an id. Thus chaining the alloc/init calls is equivalent to this:

id o = [TBCTouchDelegate alloc];
touchDelegate = [o initWithSource:self];

Hence, you are calling initWithSource: on an id-typed variable. If there's a conflicting initWithSource: method, you could get this compiler error.

Is there a conflicting method? I checked the system, and the only conflicting one was in NSAppleScript:

- (id)initWithSource:(NSString *)source;

Now NSAppleScript is part of Foundation, but I noticed this is iPhone code. So perhaps you only get this error when compiling for the simulator, and not the device?

In any case, if this is your problem, you could get around it by splitting alloc/init onto two different lines:

touchDelegate = [TBCTouchDelegate alloc];
touchDelegate = [touchDelegate initWithSource:self];

Now, you're calling initWithSource: on a fully-typed variable (instead of id-typed), so the compiler no longer has to guess which one to pick. Or you could cast the return from +alloc:

touchDelegate = [(TBCTouchDelegate *)[TBCTouchDelegate alloc] initWithSource:self];

Another solution is to rename initWithSource: to avoid the conflict and perhaps make it more descriptive. You don't say what the class is currently named nor what the "source" is for, so I cannot throw out any possibilities.

like image 67
Dave Dribin Avatar answered Sep 29 '22 00:09

Dave Dribin