iosobjective-cmacospointersclang

Incompatible pointer types initializing 'SubClass *__strong' with an expression of type'BaseClass *'


In objective-C, why we can not alloc+init or new a base-class object with super-class, whilst we can use constructor of super-class to initialize?

Below is some code :

s1 can be created quite comfortably.

NSMutableString *s1=[NSString string];
NSLog(@"%@",s1);

But s2 and s3 can not be, and gives a warning

Incompatible pointer types initializing 'SubClass *__strong' with an expression of type'BaseClass *'

NSMutableString *s2=[[NSString alloc] init];
NSLog(@"%@",s2);

NSMutableString *s3=[NSString new];
NSLog(@"%@",s3);

//here no warning.
id mem=[NSString alloc];
NSMutableString *s4=[mem init];
NSLog(@"%@",s4);

What happens when we break alloc + init to two different statement?


Solution

  • The answer can be found in Objective-C Features of the Clang 3.3 documentation:

    Related result types

    According to Cocoa conventions, Objective-C methods with certain names (“init”, “alloc”, etc.) always return objects that are an instance of the receiving class’s type. Such methods are said to have a “related result type”, meaning that a message send to one of these methods will have the same static type as an instance of the receiver class.

    Therefore in

    NSMutableString *s2 = [[NSString alloc] init];
    

    the type of the right hand side is actually NSString * and not id, and assigning that to an NSMutableString * gives a "Incompatible pointer types" warning.

    On the other hand, the string method in

    NSMutableString *s1 = [NSString string];
    

    does not have a "related result type", so it just returns an id which can be assigned to the NSMutableString *.

    Breaking alloc/init into separate statements suppresses the warning only if you use id as intermediate type. With NSString or NSMutableString you still get the warnings:

    NSString *tmp4 = [NSString alloc];
    NSMutableString *s4 = [tmp4 init]; // <-- Warning here
    
    NSMutableString *tmp5 = [NSString alloc]; // <-- Warning here
    NSMutableString *s5 = [tmp5 init];
    

    According to the documentation, a method has a "related result type" if its return type is compatible with the type of its class and if: