クラスを更に考える (4/8)
作成:2011-02-08 11:36
更新:2012-10-20 10:47
更新:2012-10-20 10:47
■継承とオーバーライド
続いて、前回説明した「継承」について、もう少し考えてみることにしましょう。継承は、既にあるクラスを受け継いで新しいクラスを定義することでした。MyTestClassも、NSObjectを継承していましたね。では、更にこのMyTestClassを継承して、新しいクラスを作ってみましょう。
下にそのサンプルを掲載してあります。MyTestClass.hを見てください。今度は、「SuperMyTestClass」というクラスを新たに追加してあります。そして、「message2」というインスタンス変数を新たに追加し、それにアクセスするためのメソッドも増やしました。また2つのメッセージを同時に渡せるコンビニエンスコンストラクタも用意してあります。
続いて、SuperMyTestClass.mを見てみましょう。コンビニエンスコンストラクタでは、以下のような形でインスタンスを作成していますね。
そして更にコードを見ていくと……ヘッダーには定義していなかった「printMessage」メソッドが実装されていることがわかります。このメソッドは、確かスーパークラスのMyTestClass
クラスにあったものです。それをサブクラスで再度、実装しなおしているのです。
このように、スーパークラスにあるメソッドをサブクラスで改めて実装することを「オーバーライド」といいます。オーバーライドすると、そのメソッドが呼び出されたとき、スーパークラスのメソッドではなく、新たに定義したサブクラス側のメソッドが呼び出されるようになります。結果として、「スーパークラスにあったメソッドの機能を変更する」ことができることになります。
このように、オーバーライドは「既にある機能の拡張や変更」を行うのに重要な役割を果たします。オーバーライドは、ヘッダーには定義を書く必要はありません(スーパークラスのヘッダーで既に定義済みですから)。単に実装だけを記述するだけでOKです。オーバーライドは、継承とセットで覚えておくようにしましょう。
※@autoreleasepoolについて
MyTestClassのmyTestClassWithMessage:メソッドでは、、以下のような文が記述されています。
まあ、慣れないうちは「どういう状況の時にこれが必要か」がわからないと思います。とりあえずビルドしてみて、以下のような警告が現れたら、@autoreleasepool内でインスタンスを生成するように修正する、と考えてください。
※サンプルのクラス名についてお詫び
サンプルでは、MyTestClassのサブクラスを「SuperMyTestClass」としてあります。「え? サブクラスなのにSuper? それともこっちがスーパークラスなの?」と混乱した方もいるかも知れません。わかりやすく「SubMyTestClass」などとしておくべきでした。
釈明するわけではないのですが、記事の筆者は普段、「○○クラスを継承して拡張したらSuper○○、それを更に拡張したらUltra○○、更に更に拡張したらTera○○……」というように名前をつける習慣がついてまして、つい深く考えず「Suer~」と名付けてしまってます。わかりにくいかも知れませんが、「このSuper○○のほうがサブクラスだ」と理解してくださいませ。
下にそのサンプルを掲載してあります。MyTestClass.hを見てください。今度は、「SuperMyTestClass」というクラスを新たに追加してあります。そして、「message2」というインスタンス変数を新たに追加し、それにアクセスするためのメソッドも増やしました。また2つのメッセージを同時に渡せるコンビニエンスコンストラクタも用意してあります。
続いて、SuperMyTestClass.mを見てみましょう。コンビニエンスコンストラクタでは、以下のような形でインスタンスを作成していますね。
id obj = [super myTestClassWithMessage:str];スーパークラスのコンビニエンスコンストラクタを呼び出してインスタンスを作り、それにsetMessage2:で2つ目のメッセージを追加しています。継承を使っていますので、こんな具合にスーパークラスにあるメソッドをすべて利用出来るのですね。またmyTestClassWithMessage:メソッド内ではmessageインスタンス変数に値を設定していますから、つまりmessageもちゃんと利用できていることがわかります。
[obj setMessage2:str2];
return obj;
そして更にコードを見ていくと……ヘッダーには定義していなかった「printMessage」メソッドが実装されていることがわかります。このメソッドは、確かスーパークラスのMyTestClass
クラスにあったものです。それをサブクラスで再度、実装しなおしているのです。
このように、スーパークラスにあるメソッドをサブクラスで改めて実装することを「オーバーライド」といいます。オーバーライドすると、そのメソッドが呼び出されたとき、スーパークラスのメソッドではなく、新たに定義したサブクラス側のメソッドが呼び出されるようになります。結果として、「スーパークラスにあったメソッドの機能を変更する」ことができることになります。
このように、オーバーライドは「既にある機能の拡張や変更」を行うのに重要な役割を果たします。オーバーライドは、ヘッダーには定義を書く必要はありません(スーパークラスのヘッダーで既に定義済みですから)。単に実装だけを記述するだけでOKです。オーバーライドは、継承とセットで覚えておくようにしましょう。
※@autoreleasepoolについて
MyTestClassのmyTestClassWithMessage:メソッドでは、、以下のような文が記述されています。
@autoreleasepool {
obj = [[self alloc] init];
}この@autoreleasepoolというのは、オートリリースプールにオブジェクトを追加するのに用意されているものです。Xcode 4.2より、ARCがサポートとなり、NSAutoreleasePoolの利用が禁止されました。が、このメソッドのように、クラスメソッド内でインスタンスを生成しreturnするような場合、ARCではオブジェクトの参照カウントが追いきれません。こうした場合、@autoreleasepoolを使い、明示的にオートリリースプールへ追加します。まあ、慣れないうちは「どういう状況の時にこれが必要か」がわからないと思います。とりあえずビルドしてみて、以下のような警告が現れたら、@autoreleasepool内でインスタンスを生成するように修正する、と考えてください。
Object 〇〇 of class クラス autoreleased with no pool in place
- just leaking - break on objc_autoreleaseNoPool() to debug
※サンプルのクラス名についてお詫び
サンプルでは、MyTestClassのサブクラスを「SuperMyTestClass」としてあります。「え? サブクラスなのにSuper? それともこっちがスーパークラスなの?」と混乱した方もいるかも知れません。わかりやすく「SubMyTestClass」などとしておくべきでした。
釈明するわけではないのですが、記事の筆者は普段、「○○クラスを継承して拡張したらSuper○○、それを更に拡張したらUltra○○、更に更に拡張したらTera○○……」というように名前をつける習慣がついてまして、つい深く考えず「Suer~」と名付けてしまってます。わかりにくいかも知れませんが、「このSuper○○のほうがサブクラスだ」と理解してくださいませ。
(by. SYODA-Tuyano.)
※プログラムリストが表示されない場合
AddBlockなどの広告ブロックツールがONになっていると、プログラムリスト等が表示されない場合があります。これらのツールをOFFにしてみてください。
●プログラム・リスト●
※MyTestClass.hの内容
#import <Foundation/Foundation.h>
@interface MyTestClass : NSObject {
NSString* message;
}
+(MyTestClass*)myTestClassWithMessage:(NSString*)str;
-(void)setMessage:(NSString*) str;
-(NSString*)message;
-(void)printMessage;
@end
@interface SuperMyTestClass : MyTestClass {
NSString* message2;
}
+(SuperMyTestClass*)superMyTestClassWithMessage:(NSString*) str
andMessage2:(NSString*) str2;
-(NSString*)message2;
-(void)setMessage2:(NSString*) str;
@end
※MyTestClass.mの内容
#import "MyTestClass.h"
@implementation MyTestClass
+(MyTestClass*)myTestClassWithMessage:(NSString*) str {
MyTestClass* obj;
@autoreleasepool {
obj = [[self alloc] init];
}
[obj setMessage:str];
return obj;
}
-(void)setMessage:(NSString*) str {
message = str;
}
-(NSString*)message {
return message;
}
-(void)printMessage {
NSLog(@"%@",message);
}
@end
@implementation SuperMyTestClass
+(SuperMyTestClass*)superMyTestClassWithMessage:(NSString*) str
andMessage2:(NSString*) str2 {
id obj = [super myTestClassWithMessage:str];
[obj setMessage2:str2];
return obj;
}
-(NSString*)message2 {
return message2;
}
-(void)setMessage2:(NSString*) str {
message2 = str;
}
-(void)printMessage {
NSLog(@"%@. %@",message,message2);
}
@end
※main関数の内容
int main (int argc, const char * argv[]) {
MyTestClass* obj = [MyTestClass
myTestClassWithMessage:@"This is First Object!!"];
[obj printMessage];
SuperMyTestClass* obj2 = [SuperMyTestClass
superMyTestClassWithMessage:@"This is Second Object!!"
andMessage2:@"It's SUPER!!!"];
[obj2 printMessage];
return 0;
}
※関連コンテンツ
「初心者のためのObjective-Cプログラミング入門」に戻る