Objective-Cの第四歩 インスタンスプロパティ・メソッドを定義する
公開日:
:
最終更新日:2014/06/28
iOSアプリを書く! C言語, Objective-C, Xcode, オブジェクト指向
Objective-Cの第三歩では、ピュアC言語からのObjective-Cのクラス入門ということで、C言語の関数で実装していた処理をクラスとクラスメソッドという形で書き直した。とは言え、実際のところ前回やったような書き換えはあまりクラスを持ち出してくる意味も無い。
ではどういったものがクラスにするにふさわしい処理なのだろうか。クラス導入の動機については複数考えられるが、その内の1つが、処理に個性付けされた単位を持たせたいというものがある。噛み砕いて言えば、処理の擬人化だ。ということで、前回のsummaryクラスに個性を持たせて、Studentクラスというものにしてみよう。
Objective-Cの命名規則など
その前に、補足的なものを。前回はsummaryクラスとして小文字でクラスを作成し、メソッド名はCalculateという大文字から始まるものにした。これらの名前はエラーとなることがなくコンパイルできたので、Objective-Cのコンパイラが解釈できる範囲の名前であることが分かる。
けれども、Objective-C組み込みのクラスやメソッドを眺めてみると、命名規則というものがちゃんと存在するということが分かる。したがって、ソースコードの可読性を下げないためにも、自作クラスやメソッドについてもこの命名規則と矛盾しないように命名するべきである。
Objective-Cでの命名規則は、GoogleやNew York Timesなど社内ルールを公開しているところがあるので、それらに倣ってみるのも良いだろう。ひとまず最大公約数的なところは、以下の通り。
種類 | 規則 | 例 |
---|---|---|
変数名 | 小文字から始まり、単語の区切り後から大文字(camelcase) | sampleVariable |
定数名 | 大文字から始まり、単語の区切り後も大文字 | SampleConstant |
自作定数の場合、上記ルールに加えて作成者を明示するための大文字の接頭辞をつける場合もある。そのようにすると一瞥して自作の定数であることが分かる。 | MYSampleConstant(”MY”はあらゆる自作定数の先頭につける) | |
クラス名 | 大文字から始まり、単語の区切り後も大文字 | SampleClass |
自作クラスの場合、上記ルールに加えて作成者を明示するための大文字の接頭辞をつける場合もある。そのようにすると一瞥して自作クラスであることが分かる。 | MYSampleClass(”MY”はあらゆる自作クラスの先頭につける) | |
メソッド名 | 小文字から始まり、単語の区切り後から大文字(camelcase) | sampleMethod |
プロパティ名 | 小文字から始まり、単語の区切り後から大文字(camelcase) | sampleProperty |
Objective-Cのクラスを設計する
新規コンソールアプリケーションを選び、プロジェクト名をGaussTesterとして、メニューからStudentクラスを作成しよう。今回作成するStudentクラスは、生徒名情報をもち、終項の正の整数と各項間の差を与えると、0までの各項を足し合わせて合計を教えてくれる、そんな機能にしたい。
Studentクラスにはプロパティとして生徒名が必要になる。生徒名を与えてインスタンス化するようにすると、各インスタンスが擬人化されるイメージが湧くだろう。クラスを設計する際の考え方は、こういうものでよい。
ということで、Studentクラスは生徒名を設定するコンストラクタメソッドを持つ。また、合計の計算をするメソッドと、計算した合計をコンソールに吐き出すメソッドも必要だ。したがって、次のようなインターフェイス部(Student.h)になるだろう。
#import <Foundation/Foundation.h> @interface Student : NSObject @property int numberGap,numberTarget; @property NSString *name; - (id)initWithName:(NSString *)name; - (int)summary0ToX:(int)x withGap:(int)gap; - (void)sayAnswer; @end |
新しく登場したものを上から見ていこう。@propertyというのは特殊なプロパティ宣言を行うコンパイラディレクティブ。後で実装部に出てくる@synthesizeとセットになるものだが、便利な機能なのでプロパティ宣言時には必ず書くものと覚えてしまって良いかもしれない(Objective-Cの進化も頭打ちになったわけだし)。
NSString *nameというのは、NSStringというObjective-C組み込みのオブジェクト型の、nameというプロパティを宣言している。オブジェクト型のプロパティはオブジェクトへの参照が実体となるので、*nameというようにポインタを格納している。ポインタはWEBプログラマには慣れない概念かもしれないが、メモリ上での番地のことだ。巨大なオブジェクトを取り回すのに、変数には番地だけ格納しておくと都合が良いのだ。変数名の前に”*”をつけるのが特徴。
インスタンスプロパティ・メソッドの宣言
インスタンスメソッドの宣言は、クラスメソッドの”+”ではなく、”-“を先頭につけて行う。initWithNameというものが、Studentクラスに生徒名を与えてインスタンス化を行うイニシャライザだ。他言語におけるコンストラクタの役割である。返り値の型がidとなっているが、これはどのようなオブジェクトの型でも代入できる動的オブジェクト型というものである。イニシャライザの仕組みはクラスをインスタンス化して返すというものなので、返り値がオブジェクト型でなければならないのだ。
メソッドの引数につけるラベル
次に、summary0ToXというインスタンスメソッドを見てみよう。引数は2つ、終項のxと各項間の差のgapがある。では、2つめの引数の前についているwithGapというのが何かというと、これはラベルと呼ばれるものだ。2つの引数を持つメソッドの宣言の仕方を、改めて確認してみよう。
- (返り値)メソッド名:(引数1の型)引数1 引数2のラベル:(引数2の型)引数2; /* 例:- (int)summary0ToX:(int)x withGap:(int)gap; */ |
引数が3つ以上の場合も、ラベルをつけていく。このラベルにどういう意味があるかというと、メソッドの呼び出し時に自然言語(英語)に近く分かり易くなるということである。実際にsummary0ToXメソッドに終項10、各項間の差1を与えて呼び出すと、下のような具合になる。
[self summary0ToX:10 withGap:1]; |
なるほどおそらくわかりやすいのだろう。こうしてラベルをつけることによって、コード中のコメントの頻度も減ることが期待されよう。
インスタンスプロパティ・メソッドの実装
それでは、次に実装部であるStudent.mを見てみよう。
#import "Student.h" @implementation Student @synthesize name = _name; @synthesize numberGap = _numberGap; @synthesize numberTarget = _numberTarget; - (id)initWithName:(NSString *)name{ self = [super init]; if(self){ self.name = name; } return self; } -(int)summary0ToX:(int)x withGap:(int)gap{ int i,sum=0; for(i=x;i>=1;i=i-gap){ sum += i; } return sum; } -(void)sayAnswer{ NSLog(@"%@「%dまでの正の整数を、%d刻みで足していった和は%dです。」",self.name,self.numberTarget,self.numberGap,[self summary0ToX:self.numberTarget withGap:self.numberGap]); } @end |
@propertyと@synthesizeでアクセサメソッドの実装
先程予告していた、@synthesizeが登場した。これは@propertyによる宣言を行った変数に(慣例的に)変数名の前にアンダーライン(”_”)をつけた名前のものを代入することで、変数のアクセサメソッドであるgetterとsetterを自動的に実装してくれるという仕組みである。
アクセサメソッドというのは、プロパティに代入された値を取り出すgetter、プロパティに特定の値をセットするsetterのことだ。WEB系言語でそれがどのように行われていたかというと、JavaScriptの場合getter、setterともにドット(”.”)によるアクセスができていた。
//Studentクラスのインスタンスstudentのnameプロパティへのアクセス //setter student.name = "吉田"; //getter alert(student.name); |
PHPの場合は”->”を使うか、連想配列の添字でアクセスしていた。
//Studentクラスのインスタンス$studentのnameプロパティへのアクセス //setter $student->name = "吉田"; $student["name"] = "吉田"; //getter echo $student->name; echo $student["name"]; |
こういったアクセス方法が標準で用意されているのは、これらのWEB系言語が準モダン言語だからで、古い言語などでは、プロパティに外部からアクセスするgetNameとかsetNameとかのアクセサメソッドを自力で実装する必要があった。
@propertyと@synthesizeを使うと、JavaScriptと同様のドット(”.”)によるアクセスと、標準的なブラケット”[]”内でのメソッドコール(getterはプロパティ名、setterはset+プロパティ名(頭文字は大文字に))によるアクセスの両方が提供される。
//Studentクラスのインスタンスstudentのnameプロパティへのアクセス //setter student.name = "吉田"; [student setName:@"吉田"]; //getter NSLog(student.name); NSLog([student name]); |
イニシャライザの実装
さて、インターフェイス部で宣言したイニシャライザである、initWithNameを実装しよう。標準的なイニシャライザとしては、initというメソッドがあり、インスタンスの作成は以下のようなメッセージで行う。
Student* studentA = [[Student alloc] init]; |
allocというメソッドがインスタンスの作成、それからinitが初期化処理になるわけだが、これは親クラスであるNSObjectがもつクラスメソッドである。
一方、今回のようにイニシャライザを書き換える場合、親クラスのinitメソッドを呼び出した上で、独自の処理を加えるという形で実装する。上で挙げたコードの当該部分を再掲。
- (id)initWithName:(NSString *)name{ self = [super init]; if(self){ self.name = name; } return self; } |
引数の型がNSString *となっているが、これはポインタの書き方で、NSString型のポインタを意味している。
親クラスを表すのは、superという予約語で、[super init]のようにすると、親クラスのクラスメソッドinitが呼べる。イニシャライズに成功したら、self.nameでインスタンスプロパティにアクセス(setterメソッドの使用になりますね)して値を代入し、最後にreturnしている。
これで呼び出し側では以下のように呼び出せば良くなる。
Student* studentA = [[Student alloc] initWithName:@"吉田"]; |
NSStringとNSLogの説明
前々から気になっていたけれど、吉田って誰?吉田の前の”@”は何?という疑問があるかもしれない。これは通常の文字列ではなくNSString型の文字列を表すためつけているものである。NSStringはC言語標準のchar型などと異なり、文字列処理に便利なクラスメソッドを標準で備える。感覚としては、JavaScriptなどでString型が勝手にオブジェクトでラッピングされて、string.length()のようにメソッドが使えるのに非常に近い。
NSLogという命令を、最初の”Hello, World!”の説明で用いていたが、引数の文字列の前にこの”@”がつけられていた。NSLogもNSStringを吐き出すという命令なわけだ。
そこで今度は、NSLogを使ったsayAnswerメソッドの実装を見てみよう。当該部分のコードの再掲。
-(void)sayAnswer{ NSLog(@"%@「%dまでの正の整数を、%d刻みで足していった和は%dです。」",self.name,self.numberTarget,self.numberGap,[self summary0ToX:self.numberTarget withGap:self.numberGap]); } |
ピュアC言語で書き直した”Hello, World!”の時に使ったprintfと同じように、フォーマット付の変数出力をしている。NSLogにはそういう機能もある。文字列中の%@というのは、その場所にNSString型の値が入りますということ。
今度はmain.mを見てみよう。こちらは普通のC言語アプリケーションのようなことしか行っていないのだが、NSStringのクラスメソッドを一箇所用いている。
#import <Foundation/Foundation.h> #import "Student.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... char name[100]; int target,gap; printf("どの学生に答えさせますか?\n"); scanf("%99s",name); Student* studentA = [[Student alloc] initWithName:[NSString stringWithUTF8String:name]]; printf("0からどの数字まで計算させますか?\n"); scanf("%d",&target); studentA.numberTarget = target; printf("数字間の差は?\n"); scanf("%d",&gap); studentA.numberGap = gap; [studentA sayAnswer]; } return 0; } |
stringWithUTF8Stringというメソッド。このようにscanfで得たchar型などの文字列をNSString型に変換するメソッドなどが用意されているわけだ。
プログラムをBuild GaussTester実行結果
完成したプログラムは、XcodeのProductメニューの、Buildでアプリケーションにしよう。左サイドバーのファイルツリーの、Productsフォルダの中にGaussTesterというアプリケーションができているので、ターミナルを立ち上げて実行する。
まあこのようにアプリケーションが完成する。今回はかなりボリュームの多い内容だったけれど、前回の意味の無いアプリケーションとは異なり、段々ちゃんとしたアプリケーションになってきたでしょ?世のアプリケーションなんて全部これの延長です。
関連記事
-
Objective-Cの第一歩(仕切り直し)
前回Objective-Cの第一歩として挙げたコードであるが、綺麗さっぱり忘れてほしい。というのは、
-
Objective-Cでは何故.hファイルと.mファイルを作成させられるのか
前回説明したように、Objective-Cでプログラムを書く際には、別にC言語そのもののつもりで書い
-
Objective-CでiOSアプリの第一歩 新規プロジェクトで生成されるコードの解説
しけたコンソールアプリばかり作っていたけれど、ようやっとiOSプログラミングに入るとしよう。急がない
-
Objective-CでiOSアプリの第二歩 とにかく立ち上がればというレベルのHello, World!アプリを作成
前回はiOSアプリ作成の第一歩として、Empty Applicationを選択した際に書いてあるコー
-
Objective-CでiOSアプリの第三歩 UIViewControllerの必要性を理解する
前回はUIViewControllerの存在を無視して"Hello, World!"アプリケーション
-
iOSプログラミング入門書では、まず最初のHello, World!すら難しい
Hello, World!が第一歩 新しくプログラミング言語を習得するときに、「この言語でのH
-
Xcode 6の正式リリースまでに、Objective-Cでアプリを一つ仕上げる!
Swiftに早速触れてみたい!でもXcode 6がベータ版の間は、有料(7800円/年)のDevel
-
Objective-Cの第三歩 クラスとクラスメソッドを定義する
Swiftの事をひとまず置いておいてObjective-Cの入門編を書いているわけだが、前々回の"H
-
Objective-Cの第六歩 プロトコルの宣言と実装
Objective-Cの第五歩では、親クラスの継承とメソッドのオーバーライドについて説明した。そこま
-
Objective-Cの第五歩 クラスの継承とメソッドのオーバーライド
前回Objective-Cの第四歩では、等差数列の和を計算するプログラムGaussTesterの作成