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?
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:
- the first word is “alloc” or “new”, and the method is a class method, or
- the first word is “autorelease”, “init”, “retain”, or “self”, and the method is an instance method.
First case
NSMutableString *s2=[[NSString alloc] init];
NSLog(@"%@",s2);
Here you are creating an NSString
instance and send init
message on it and finally you are assigning an NSString
instance to NSMutableString
. Here compiler knows that you are assigning an incompatible pointer types. So its warns you.
Second case
id mem=[NSString alloc];
NSMutableString *s3=[mem init];
You are creating an NSString
instance and assigned as type id
. and send init
on an object of type id
. id type is generic, compiler doesn't know its actual type till run time(dynamic typing). So it wont warn you. So problem is not in nested call.
Finally its my observation only :).