何にはまったかって言うと、initを実装するときに、Javaのコンストラクタと同じノリで実装して痛い目見たって話です。
ちょっと長くて読みにくくなっちゃいますが、はまったコードのサンプルを全文載せます。
なお、このコード自体は「Objective-Cってスーパークラスのインスタンスをサブクラスにキャストできるんじゃないのか?」と思って試したコードです。
そしたら、その検証以前にこの投稿の主題で言っている痛い目をみたわけです。
では、まずは思ったように動かなかったのがコレ。
init_returns_nothing.m
# include <Foundation/NSObject.h>
# include <stdio.h>
// testForSuper2Sub.m
// To compile this program in your console.
// Put this command
// gcc testForSuper2Sub.m -framework Foundation
@interface SuperClass : NSObject {
}
@property int superValue;
@end
@implementation SuperClass
@synthesize superValue = _superValue;
-(id) init {
self = [super init];
if(self != nil){
NSLog(@"super init");
self.superValue = 0;
}
}
@end
@interface SubClass : SuperClass {
}
@property int subValue;
@end
@implementation SubClass
@synthesize subValue = _subValue;
-(id) init {
self = [super init];
if (self != nil) {
NSLog(@"sub init");
self.superValue = 1;
self.subValue = 2;
}
}
@end
@interface CanSuperClassInstanceCastToSubClass : NSObject {
}
@property (retain) SuperClass *myObject;
@end
@implementation CanSuperClassInstanceCastToSubClass
@synthesize myObject = _myObject;
-(id) init {
self.myObject = [[SubClass alloc] init];
}
@end
int main(void){
CanSuperClassInstanceCastToSubClass *super2sub = [[CanSuperClassInstanceCastToSubClass alloc] init];
SubClass *mySub = (SubClass *) super2sub.myObject;
NSLog(@"superValue:%d",mySub.superValue);
NSLog(@"subValue:%d",mySub.subValue);
}
そして、それを修正したのが、コレです。
init_returns_the_object.m
# include <Foundation/NSObject.h>
# include <stdio.h>
// testForSuper2Sub.m
// To compile this program in your console.
// Put this command
// gcc testForSuper2Sub.m -framework Foundation
@interface SuperClass : NSObject {
}
@property int superValue;
@end
@implementation SuperClass
@synthesize superValue = _superValue;
-(id) init {
self = [super init];
if(self != nil){
NSLog(@"super init");
self.superValue = 0;
}
return self;
}
@end
@interface SubClass : SuperClass {
}
@property int subValue;
@end
@implementation SubClass
@synthesize subValue = _subValue;
-(id) init {
self = [super init];
if (self != nil) {
NSLog(@"sub init");
self.superValue = 1;
self.subValue = 2;
}
return self;
}
@end
@interface CanSuperClassInstanceCastToSubClass : NSObject {
}
@property (retain) SuperClass *myObject;
@end
@implementation CanSuperClassInstanceCastToSubClass
@synthesize myObject = _myObject;
-(id) init {
self = [super init];
if(self != nil){
self.myObject = [[SubClass alloc] init];
}
return self;
}
@end
int main(void){
CanSuperClassInstanceCastToSubClass *super2sub = [[CanSuperClassInstanceCastToSubClass alloc] init];
SubClass *mySub = (SubClass *) super2sub.myObject;
NSLog(@"superValue:%d",mySub.superValue);
NSLog(@"subValue:%d",mySub.subValue);
}
どこが違うかって言うと、initの実装です。
全部に下のリターンがついているんですね。
return self;
initってのはJavaコンストラクタと同じみたいに考えていたためにやらかしているんですが、上のようにちゃんとreturnを書かないと何も返ってこないんですね。
-(id)でやっているからなんでしょうけど、コンパイルエラーにもなんにもならないので恐ろしいことです。
ちなみに、このコードで検証したかった、
「Objective-Cってスーパークラスのインスタンスをサブクラスにキャストできるんじゃないのか?」ってのはそのとおりでした。
色々発想の斜め上でおじさんビックリだ。