Objective-Cのクラス定義を理解しようCocoaの素、Objective-Cを知ろう(3)(3/3 ページ)

» 2008年11月10日 00時00分 公開
[竹下肯己株式会社 qnote]
前のページへ 1|2|3       

アクセサメソッドの自動生成

 先ほどの例では、クラスの中にプロパティが1つしか定義されていませんでしたが、実際にはクラスにはいくつもの(時には数十個もの)プロパティが定義されることもあります。そのようなとき、すべてのプロパティにゲッターメソッドとセッターメソッドを定義していくのは、非常に手間が掛かる作業です。また、コピー&ペーストが多用され、コーディングミスの原因ともなります。

 Objective-Cのバージョン2.0からは、プロパティのアクセサメソッドを自動生成させるためのコンパイラディレクティブが導入されました。以下に簡単な例を見てみましょう。













a
a
a







b











#import <Foundation/NSObject.h>
#import <Foundation/NSString.h>
#import <stdio.h>

/* クラスの宣言部 */
@interface MyClass : NSObject {
    // インスタンス変数
    int myInt;
    NSString *myStr1, *myStr2, *myStr3;
}

// プロパティの宣言
@property int myInt;
@property (assign) NSString *myStr1, *myStr2;
@property (retain, readonly) NSString *myStr3;

@end

/* クラスの実装部 */
@implementation MyClass

// アクセサメソッドの生成
@synthesize myInt, myStr1, myStr2, myStr3;

@end

/* メソッドの実行例 */
int main(void) {
    MyClass *test = [[MyClass alloc] init];
    test.myStr1 = @"aiueo";
    NSString *myString = test.myStr1;
    printf("%s \n", [myString UTF8String]);

    return 0;
}
main.m

(a) プロパティの宣言

 まず、クラスの宣言部分で、「@property」というコンパイラディレクティブを利用して、インスタンス変数に対応するプロパティを宣言します。変数の型が同じであれば、カンマつなぎで複数指定することができます。

 @propertyの後の()内の記述は、アクセサメソッドの生成内容を指示するオプションです。以下のような項目を、カンマつなぎで指定できます(以下の解説中に登場するプログラムの「メモリ管理」については、連載の別の回で詳しく解説する予定です)。

[セッターメソッド内での変数の扱い方についての指定]

  • assign
     セッターメソッドが受け取った変数を、クラスのインスタンス変数に単純に代入させる場合に指定します。なお、その変数がオブジェクト型でない(int型など)の場合にはassignしか指定できないので、指定そのものを省略できます。
  • retain
     セッターメソッドが受け取った変数を、クラスのインスタンス変数にセットするとき、その変数が勝手にメモリ上から破棄されてしまわないように、必要な手続きを実行させておきたいときに指定します。retainは、プロパティの型がオブジェクト型で、かつプログラムのメモリ管理を自動では行わない場合に有効です。
  • copy
     セッターメソッドが受け取った変数を、クラスのインスタンス変数にセットするとき、引数のコピーを作ってセットさせたいときに指定します。copyは、プロパティの型がオブジェクト型の場合に有効です。

[セッターメソッドを生成するかどうかの指定]

  • readwrite
     セッターメソッド、ゲッターメソッドの両方を生成します。これはデフォルト値なので省略可能です。
  • readonly
     ゲッターメソッドのみを生成します。

 このほか、セッターメソッドやゲッターメソッドのメソッド名の指定や、マルチスレッド(並行処理)への対応をあえてさせたくないときの指定などのオプションもありますが、あまり使用頻度は高くないので、ここでは割愛します。

(b) アクセサメソッドの生成

 クラスの実装側では、「@synthesize」というコンパイラディレクティブで、先ほど宣言したプロパティへのアクセサメソッドを生成するよう指示します。複数のプロパティをカンマつなぎで指定することができます。

クラスのインスタンス生成と初期化

 これまで、クラスを利用するときは、おまじないのように [[クラス名 alloc] init] と記述してきました。ここで改めて、クラスのインスタンスの生成と初期化について解説しておきます。

 まず「alloc」メソッドですが、これは文字通りクラスのインスタンス(実体)を生成し、メモリ上に割り当てるためのメソッドです。これはルートクラスであるNSObjectに定義されています。当然インスタンスがまだ存在しない段階で実行されるメソッドとなりますので、クラスの型から直接実行できるクラスメソッドとして定義されています。戻り値は、生成(メモリ割り当て)されたクラスのインスタンスです。

 次に、allocメソッドの戻り値である生成したてのインスタンスに対し、初期化処理を実行します。これが「init」メソッドです。initメソッドは「イニシャライザ」と呼ばれ、基本的な機能はやはりNSObjectのinitメソッドとして実装されています。initメソッドの戻り値は、初期化を済ませたクラスインスタンスです。

 そのクラスで独自に必要な初期化処理がある場合には、initメソッドを上書き(オーバーライド)します。また、initメソッドは引数を取らないので、もしインスタンスの初期化に引数が必要な場合は、「initWithXxx」といったネーミングルールで、引数ありのイニシャライザを別途定義する必要があります。以下に簡単な例を見てみましょう。

























a
b
c

d




a
b
c

d

















#import <Foundation/NSObject.h>
#import <stdio.h>

/* クラスの宣言部 */
@interface MyClass : NSObject {
    // インスタンス変数
    int myVar;
}

@property int myVar;

// イニシャライザの宣言
- (id)init;                     // 引数なし
- (id)initWithMyVar:(int)value; // 引数あり

@end

/* クラスの実装部 */
@implementation MyClass

@synthesize myVar;

// 引数なしのイニシャライザの実装
- (id)init {
    self = [super init];
    if (self != nil) {
        self.myVar = 3;
    }
    return self;
}

// 引数ありのイニシャライザの実装
- (id)initWithMyVar:(int)value {
    self = [super init];
    if (self != nil) {
        self.myVar = value;
    }
    return self;
}

@end

/* インスタンス生成の実行例 */
int main(void) {

    // 引数なしでインスタンスを生成
    MyClass *test1 = [[MyClass alloc] init];
    printf("%d \n", test1.myVar);

    // 引数を渡してインスタンスを生成
    MyClass *test2 = [[MyClass alloc] initWithMyVar:5];
    printf("%d \n", test2.myVar);

    return 0;
}
main.m

 上記のクラスには、インスタンス変数を既定値で初期化する「init」メソッドと、指定された引数で初期化する「initWithMyVar:」メソッドという2つのイニシャライザが定義されています。

 イニシャライザの実装では、まず親クラス(super)のイニシャライザを実行し、親クラス側として必要な初期化処理を済ませたインスタンスを取得します(a)。万が一初期化に失敗すると、nil(空っぽのオブジェクト)が返されます(b)。無事にインスタンスが取得できたら、そのインスタンスに対し、このクラス自身で独自に必要となる初期化処理を行います(c)。そしてすべての初期化処理の済んだインスタンスを戻り値として返します(d)。

 これで、上記の実行例にあるように、必要に応じてふさわしいイニシャライザを実行してインスタンスを初期化することができるようになります。

 今回は、Objective-Cのクラス定義について詳しく整理してみました。実際にもっと複雑なプログラムを作成していくときには、クラスの継承、クラスインスタンスの比較やコピー、さらにメモリ管理といったトピックを理解しておく必要があります。これらについては、少し難解なお話になりますので、連載のもう少し後の回で触れたいと思います。


前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。