Objective-C インスタンス変数へアクセスする.
話が前後している気がしますが、前回、インスタンス変数のアクセス制限の話をしたので、今回はインスタンス変数へアクセスする方法を見ていきます。
誰がアクセスできるのか
もちろんそれを保持するオブジェクトはその変数を直接用いてアクセスできます。前回の、MySampleClassを見てみましょう。
/*MySampleClass.h*/ #import <Foundation/Foundation.h> @interface MySampleClass : NSObject { float tall; UInt16 jobType; @private float salary; float weight; @protected int age; NSString* address; @public BOOL gender; NSString* name; UInt16 hairStyle; @protected UInt16 hobby; } @end /*MySampleClass.m*/ #import "MySampleClass.h" @implementation MySampleClass -(id)init { if( (self=[super init]) ) { name = @"Jimmy"; age = 25; } return self; } -(void) printAge { NSLog(@"My age is %d", age); } @end
「 -(id) init 」はMySampleClassを初期化するためのメソッドでイニシャライザと呼ばれるものです。クラスをインスタンス化する際に呼びます。
すなわち、このクラスが一つのオブジェクトとしてプログラム内で作られた際、このクラスはageに”25″、nameに”Jimmy”という値を代入します。
そして、”printAge”メソッドでは、自分のageにアクセスしその値を出力します。
このように、オブジェクトはそれ自身のインスタンス変数を自由に読み書きできます。当たり前のことですが…。
では、他のオブジェクトのインスタンス変数へアクセスするにはどのようにすればいいのでしょうか。
基本的にはObjective-Cでは、オブジェクトの外からインスタンス変数に直接アクセスすることを許していません。しかし、自分と同じクラスのオブジェクトへは「->」演算子でアクセスできます。
先ほどのプログラムを変更し、同じクラスのオブジェクトにアクセスしてみます。また、無駄な部分は省きました。
/*MySampleClass.h*/ #import <Foundation/Foundation.h> @interface MySampleClass : NSObject { int age; NSString* name; } -(id) initWithName:(NSString*)s; -(void) printFriendName:(MySampleClass*)myFriend; @end /*MySampleClass.m*/ #import "MySampleClass.h" @implementation MySampleClass -(id)initWithName:(NSString*)s { if( (self=[super init]) ) { age = 25; name = s; } return self; } -(void) printFriendName:(MySampleClass*)myFriend { NSLog(@"My friend is %@.", myFriend->name); } @end /*main.m*/ #import "MySampleClass.h" int main(void) { MySampleClass* jimmy = [[MySampleClass alloc]initWithName:@"Jimmy"]; MySampleClass* emily = [[MySampleClass alloc]initWithName:@"Emily"]; [jimmy printFriendName:emily]; [jimmy release]; [emily release]; return 0; }
メイン関数でまず2つのMySampleClassオブジェクト「jimmy」と「emily」を作っています。initがinitWithNameになっていますが、initと同じくイニシャライザとしてここでは働いています。名前を設定し、オブジェクトに違いをつけるために、このようなイニシャライザにしました。
オブジェクトを作った後、jimmyにprintFriendNameメソッドを実行させています。
では、本題のMySampleClassの実装部を見て行きましょう。
“initWithName”は先程も述べたとおり、イニシャライザで文字列を受け取り、それを自分のインスタンス変数”name”に代入しています。これで、「Jimmy」と「Emily」という違うname値をもつオブジェクトができます。
32行目の”printFriendName”は同じクラスの違うオブジェクトを受け取り、そのオブジェクトのインスタンス変数nameにアクセスし、それを出力しています。このアクセスに「->」演算子を用いています。
同じクラスの違うオブジェクトとはここでいう、「Jimmy」からみた「Emily」です。JimmyがEmilyのnameにアクセスしています。
このようにアクセスできるのは彼らが同じクラスだからで、同じインスタンス変数を持っているからです。例えスーパークラスでも、他のクラスのインスタンスは、この方法では参照できません。
他のクラスのインスタンス変数へアクセスする方法
前項のように、インスタンス変数へアクセスできるのはそのクラスのオブジェクトだけでした。しかし、プログラムを書いている上で、他のクラスのインスタンス変数を変更したり、参照したいケースは多々あります。そのようなときはどうすればいいのでしょうか。
ヒントは先程のプログラムのイニシャライザにあります。
先ほどのプログラムでは、名前を変えるために”initWithName”をいうイニシャライザを用いました。そして、メイン関数からでもメソッドを介せばインスタンス変数を書き換えることができていますね。メソッドを介せば他のクラスからでも、間接的にインスタンス変数にアクセスできるのです。
Objective-Cでは通常、インスタンス変数の値を参照したり書き換える場合は、それ専用のメソッドを用意し、そのメソッドにメッセージを送ることで、インスタンス変数へのアクセスを可能にします。
このようなメソッドを「アクセサ」と言います。また、アクセサは参照するためのメソッドと、値を変更するためのメソッドに分けられます。
参照するためのメソッドは値をゲットするので「ゲッタ(getter)」と呼ばれます。一方、値を変更するメソッドは、セットするので「セッタ(setter)」と呼ばれています。
例えば、インスタンス変数”age”のアクセサは以下のようになります。
/*ゲッタ*/ -(int) age { return age; } /*セッタ*/ -(void) setAge:(int)value { age = value; }
通常ゲッタのメソッド名はそのインスタンス変数と同じにします。他のプログラミング言語ではよく「get〇〇」という名前になっていますが、CocoaのObjective-Cでは付けないのが通例のようです。セッタのメソッド名は、インスタンス変数の前に”set”をつけ、インスタンス変数の頭文字を大文字にします。
先ほどのプログラムを変更し、メイン関数でオブジェクトを作った後、メイン関数から年齢を設定してみましょう。そして、”printFriendInfo”メソッドを追加し、与えられたオブジェクトの名前と年齢を出力させます。
/*MySampleClass.h*/ #import <Foundation/Foundation.h> @interface MySampleClass : NSObject { int age; NSString* name; } -(id) initWithName:(NSString*)s; -(void) printFriendName:(MySampleClass*)myFriend; -(void) printFriendInfo:(MySampleClass*)myFriend; -(int) age; -(void) setAge:(int)value; @end /*MySampleClass.m*/ #import "MySampleClass.h" @implementation MySampleClass -(id)initWithName:(NSString*)s { if( (self=[super init]) ) { age = 25; name = s; } return self; } -(void) printFriendName:(MySampleClass*)myFriend { NSLog(@"My friend is %@.", myFriend->name); } -(void) printFriendInfo:(MySampleClass*)myFriend { NSLog(@"My friend is %@, %d years old.", myFriend->name, [myFriend age]); } /*アクセサ*/ /*ゲッタ*/ -(int) age { return age; } /*セッタ*/ -(void) setAge:(int)value { age = value; } @end /*main.m*/ int main(void) { MySampleClass* jimmy = [[MySampleClass alloc]initWithName:@"Jimmy"]; MySampleClass* emily = [[MySampleClass alloc]initWithName:@"Emily"]; [emily setAge:20]; [jimmy printFriendInfo:emily]; [jimmy release]; [emily release]; return 0; }
初めのイニシャライズでは、28行目でage = 25; としているので、ageはどのオブジェクトも25に設定されています。そこで、メイン関数の「 [emily setAge:20]; 」でemilyの年齢をageのセッタを使って設定しています。
これを実行すると、
「My friend is Emily, 20 years old.」
と出力されます。
41行めの「 [myFriend age] 」はゲッタです。ここではnameのように「->」でアクセスできますが、せっかくなのでゲッタを使ってみました。
以上でひと通りインスタンス変数へのアクセスの仕方を説明できました。
しかし、インスタンス変数がたくさんありそれらにアクセスしたい場合、全てにアクセサを作っていくのは大変ですね。これらを省略するために、Objective-C 2.0では新たに「宣言プロパティ」という概念が追加されました。しかし、これはアクセスだけでなく様々な機能があるので、もう少しObjective-Cの理解が深まってから解説したいと思います。
今回はここまで。
2 Comments
Pingbacks