アーカイブ

‘たのしいCocoa’ タグのついている投稿

【たのしいCocoa】04_Objective-Cの文法

2008-12-21

【たのしいCocoa】04_Objective-Cの文法

たのしいCocoaプログラミング[Leopard対応版]」を元に、要約メモしておきたいと思います。

チュートリアル: 文法の実験のためのプロジェクト

Objective-C の実験のためのプロジェクト作成

  1. Xpre lang=”objc” line=”1″ を起動する
  2. プロジェクト作成する 「ファイル > 新規プロジェクト」メニューを選択。Command Line Utility カテゴリの下にある
    Foundation Tool を選択。登録されているファイルの .m の拡張子の付いたファイルが Objective-C のコードを書くためのもの。
  3. クラスを作成する 「ファイル > 新規ファイル」メニューを選択し、Cocoa カテゴリの下にある Objective-C class を選択。ここでのファイル名が、新規クラス名になる。.h も同時に作成しておく。 ※以前 AppController というクラスを作ったのと同様の手順になる。
  4. MyObject.h を編集する

    1
    2
    3
    4
    5
    6
    7
    
    #import <Cocoa/Cocoa.h>
          @interface MyObject : NSObject {
          int	count;
          }
          - (int) count;
          - (void) setCount:(int)cnt;
          @end

  5. MyObject.m を編集する

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    #import "MyObject.h"
          @implementation MyObject
          - (int) count
          {
           return count;
          }
          - (void) setCount:(int)cnt
          {
           count = cnt;
          }
          @end

  6. ObjCTest.m を編集する ここまでで作った Myobject クラスをインスタンス化して、メソッドを呼び出してみるため、ObjCTest.m を以下のように編集する。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    #import <Foundation/Foundation.h>
          #import "MyObject.h"
     
          int main (int argc, const char * argv[]) {
           NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
     
           MyObject* object = [[MyObject alloc] init];
           [object setCount:5];
     
           int count;
           count = [object count];
           NSLog (@"count is %d", count);
     
           [object release];
           [pool release];
           return 0;
          }

  7. ビルドと実行をする 「ビルド > ビルドと実行」メニューを選択する。次のような結果が表示される。
    1
    2
    3
    
        [Session started at 2008-00-00 00:00:00 +0900.]
          2008-00-00 00:00:00.000 ObjC Test[2767:10b] count is 5
        The Debugger has exited with status 0.

Objective-Cの文法

Objective-Cの書き方

■ クラスの宣言と実装

クラスの宣言ファイルには .h という拡張子(header の意味)を付ける。クラスの実装には .m の拡張子(method の意味)を付ける。

クラスの宣言

フレームワークのクラス宣言の読み込み

#import という文を使ってクラスの宣言を読み込む。例えば Cocoa のすべてのクラス宣言を読み込む場合は次のように書く。これは、「システムにある Cocoa フレームワークの Cocoa.h ファイルを読み込む」という意味になる。<> はシステムのヘッダファイルを意味する。

1
#import <Cocoa/Cocoa.h>

自分で作ったクラス宣言の読み込み

自分で作ったクラスの宣言ファイルを読み込む場合は次のように書く。“” はヘッダファイルがプロジェクトの内部にあることを示している。

1
#import "AppController.h"

クラス宣言の先頭には #import

クラス宣言を書く時は、先頭に

1
#import <Cocoa/Cocoa.h>

と書いておき、必要なものがあればそれも読み込んでおく。

■ クラスの宣言

クラスの名前を、たとえば MyObject とする。親クラスも決めておく。何かのクラスを拡張するときはそのクラスを、特にないときは Cocoa のルートクラスである NSObject を使う。

1
2
@interface MyObject : NSObject
  @end

まず、これからクラス宣言をするという意味の @interface を書く。その次にクラス名、そしてコロンで区切って親クラス名を書く。クラスの宣言は @end で終わる。

■ インスタンス変数の宣言

クラスの宣言にインスタンス変数の宣言を加えることができる。例えば先ほどの MyObject に count と index という変数を加えてみる。

1
2
3
4
5
6
@interface MyObject : NSObject
  {
   int count;
   int index;
  }
  @end

@interface と @end の間に {} を付け加えて、その中に書く。

■ メソッドの宣言

引数なしのインスタンスメソッドの場合

例えばインスタンス変数である count の値を取得するためのメソッドを、count という名前で宣言する。

1
2
3
4
5
6
7
@interface MyObject : NSObject
  {
   int count;
   int index;
  }
  - (int)count;
  @end

まず先頭に -(マイナス)が来る。空白を挟んでカッコの中に返り値の型を書く。そしてメソッドの名前を書く。

引数1つのインスタンスメソッドの場合

1
2
3
4
5
@interface MyObject : NSObjext
  ...
  - (int)count;
  - (void)setCount:(int)count;
  @end

– (void)setCount:(int)count; というメソッドが、引数を1つ取るもの。返り値とメソッド名までは引数なしのものと同じ。その後が引数のためのものとなる。まず、:(コロン)が来る。そして、カッコの中に引数の型。次に引数の名前を書く。

引数2つ以上のインスタンスメソッドの場合

1
2
3
4
5
6
@interface MyObject : NSObject
  ...
  - (int)count;
  - (void)setCount:(int)count;
  - (void)setCount:(int)count andIndex:(int)index;
  @end

1つめの引数までは前と同じ。そしてその後にまず空白がある。そして、ラベルと呼ばれるものが来る。上記の例だと andIndex がラベルになる。次にコロン、カッコして引数型、引数名と続く。ラベルはメソッド名の一部で、メソッドの正式な名前はメソッドとラベルをあわせたものになる。それらはコロンでつなぐ。上記のメソッドは「setCount:andIndex:」という名前になる(最後のコロンも含む)。コロンの後には引数が来る。だから引数の数とコロンの数は等しくなる。引数の数がさらに増えると、「ラベル:(引数型)引数名」の組が、後に増えていくことになる。

例)Cocoaのメソッド

1
2
3
4
5
6
7
8
9
10
- (id)initWithBitmapDataPlanes:(unsigned char**)planes
   pixelsWide:(int)width
   pixelsHigh:(int)height
   bitsPerSample:(int)bps
   samplesPerPixel:(int)spp
   hasAlpha:(BOOL)alpha
   isPlanar:(BOOL)isPlanar
   colorSpaceName:(NSString*)colorSpaceName
   bytePerRow:(int)rowBytes
   bitsPerPixel:(int)pixelBits;

まずコロンの数を確認する。10個あるので、10個の引数があるということになる。それぞれのコロンの前にあるのがラベル。これが引数の説明になる。後にあるのが引数の型と名前。

クラスメソッドの場合

メソッドにはクラスメソッドもある。インスタンスメソッドの宣言は、常に -(マイナス)で始まっていたが、クラスメソッドは +(プラス)になる。

1
2
3
4
@interface MyObject : NSObject
  ...
  + (int)maxIndexValue;
  @end

クラスの実装

クラス宣言は .h に書いたが、クラスの実装は .m に書く。

■ クラスの実装

まずは先ほど作ったクラスの宣言を読み込んでおく。そして、クラスの実装には @implementation を使う。

1
2
3
#import "MyObject.h"
  @implementation MyObject
  @end

■ メソッドの実装

例として、MyObject に count メソッドの実装を付け加えてみる。

1
2
3
4
5
6
7
#import "MyObject.h"
  @implementation MyObject
  - (int)count
  {
   return count;
  }
  @end

メソッドの実装は、@implementation@end の間に書いていく。まず宣言と同じ形でメソッドを書く。その後に {} を書く。その中にメソッドの処理を実装していく。

■ メソッドの宣言は常に必要か?

メソッドの宣言なしにいきなり実装しても構わない。他のクラスから呼んでもらう必要のないものに関しては、メソッドの宣言は .h ファイルに加えなくてもいい。

オブジェクトのための変数型

■ インスタンスオブジェクトのため変数型

このような値のための変数は、クラス名のポインタという形式の型になる。たとえば先ほどの MyObject クラスのインスタンスのための変数は次のようになる。

1
MyObject* object;

Cocoa のクラスである NSString のための変数は次のようになる。

1
NSString* string;

■ id 型

id 型は、全てのオブジェクトのために使える型。Cocoa の全てのクラスは NSObject から継承されているが、この NSObject には、Objective-C の全てのオブジェクトに共通するデータが含まれている。これを表すための変数型が id 型。すべてのオブジェクトは id 型の変数に代入することができる。でもそれだと、その変数がいったい何のクラスだったのか分かりにくいので、それを避けたいときはクラス名のポインタ型を使うことになる。

1
IBOutlet id textField;

では id 型が使われていたが、これを

1
IBOutlet NSTextField* textField;

と書くこともできる。こうすると何のクラスなのか分かりやすくなる。

■ nil 型

C言語では NULL という値があったが、Objective-C にも似たようなものがる。オブジェクトのための変数が、何のオブジェクトも指し示していないことを表す変数、nil という値。

1
2
NSString* string;
  string = nil;

これで、この string という変数は何も指し示していないということが保証される。

■ オブジェクトの変数と条件式

条件式は、if 文などで条件を判断するときに使う式。無効なオブジェクトを表す nil は、実際は 0 の値で、string = nil; という文は、string0 という値を代入している。これを利用すれば、あるオブジェクトの変数が有効なのか無効なのか判断するには、次のようにかけばいいということになる。

1
2
3
4
5
6
7
8
9
10
NSString* string;
  //string オブジェクトを、どこからか取得する
  ...
  //string オブジェクトが有効かどうか判断する
  if (string) {
   //string オブジェクトが有効
  }
  else {
   //string オブジェクトは無効
  }

メソッドの呼び出し

■ インスタンスメソッドの呼び出し

先ほど、MyObject クラスに count というメソッドを実装したが、それを呼び出してみる。

1
2
3
4
5
6
MyObject* object;
  //MyObject をインスタンス化する
  ...
  //count メソッドを呼び出す
  int count;
  count = [object count];

まず、返り値がある。メソッドは関数と同じように返り値を返す。そして [] がある。その中に、まずインスタンスオブジェクト。空白を開けてメソッド名を書く。

引数が1つの setCount; 、引数が2つの setCount:andIndex を呼び出す場合は次のようになる。

1
2
[object setCount:5];
  [object setCount:12 andIndex:6];

引数1つの場合はメソッド名の後に引数を、2つの場合はメソッド名とラベルの後にそれぞれ引数を書く。コロンの前にあるのが引数の説明、後にあるのが引数の値。

■ クラスメソッドの呼び出し

文法自体はインスタンスメソッドのものと変わらない。違いは、呼び出しの対象となるオブジェクト。クラスメソッドは、特定のインスタンスに対してではなく、クラス全体に対して呼び出す。なので呼び出す対象のところにクラス名を指定する。

1
2
int maxIndex;
 maxIndex = [MyObject maxIndexValue];

■ オブジェクトが nil の場合

インスタンスメソッドの呼び出しのとき、インスタンスのオブジェクトのための変数を使っていたが、これが普通にインスタンス化されたオブジェクトを指していれば問題ないが、ここに nil が入っているとどうなるか。nil に対してメソッドを呼び出すと、その呼び出しは単に無視される。返り値としては、型にかかわらず 0 が返ってくる。変数に nil が入っていると、クラッシュも何も起きない。

命名規則

■ クラスの命名規則

最初の文字は大文字

1
2
3
MyObject
NSString
WebView

プロジェクトで共通の頭文字

例えば、Cocoa に登場するクラスはすべて NS で始まっている。Web Kit の場合は Web で始まっている。このように、2文字から4文字程度の共通する文字で始める。こうやっておくと、他のプロジェクトのクラスと名前がかぶらないという利点もある。

■ インスタンス変数の命名規則

最初の文字は小文字

1
2
3
index
count
description

アンダースコアで始めるときは注意

インスタンス変数とメソッドの内部で宣言した変数とを区別するために、インスタンス変数を _(アンダースコア)で始めるというルールがある。

1
2
_index
_count

ただし、Cocoa のクラスが宣言しているインスタンス変数もアンダースコアで始まっているので、自分で宣言した変数が Cocoa のものとかぶってしまう可能性がある。

メソッドの命名規則

最初の文字は小文字

最初の文字は小文字にする。単語をつなぐときは、アンダースコアではなく、単語の頭を大文字にする。

取得と設定のためのメソッド

値を設定するメソッドの名前は、設定する属性の名前の前に set を付ける。例えば count を設定するためのメソッドは setCount、index を設定するためのメソッドは setIndex 。 それに対して取得の方は、属性の名前がそのままメソッド名になる。count を値を取得するメソッドは count、index は index 。get は付けないのが Cocoa スタイル。

英文として読める

Cocoa のメソッド名が長いのは、メソッドとラベルをつなげると英文として読み取れるようにしているため。読めるメソッド名を使うのが Cocoa の特徴。

1
2
3
4
5
6
7
8
initWithFormat:
(指定した)フォーマットを使って初期化する
 
stringByAppendingFormat:
(指定した)フォーマットを追加した文字列(を取得する)
 
canBeConvertedEncoding:
(指定した)エンコーディングに変換できるか?

Hello World を読み直す

■ AppController.h

1
2
3
4
5
6
7
8
/* AppController */
  #import <Cocoa/Cocoa.h>
  @interface AppController : NSObject
  {
   IBOutlet id textField;
  }
  - (IBAction)sayHello:(id)sender;
  @end

1
/* AppController */

コメント文。

1
 #import <Cocoa/Cocoa.h>

#import を使って Cocoa クラスの宣言を読み込んでいる。

1
{

AppCntroller クラスのための、インスタンス変数の宣言の始まりを表す中カッコ。

1
 IBOutlet id textField;

AppController クラスのためのインスタンス変数。IBOutlet という宣言は、この変数が Interface Builder で追加されたアウトレットであることを示している。次に来るのは変数の型。この変数の型は id 型ということになる。最後に来るのは変数の名前、textField 。これは Interface Builder で付けた名前だが、クラスのインスタンス変数となるので、名前付けの規則に従うようにする。

1
}

インスタンス変数の宣言の終わりを表す中カッコ。

1
 - (IBAction)sayHello:(id)sender;

AppController クラスのメソッド宣言。- で始まっているので、インスタンスメソッド。メソッドの返り値の型は IBAction になっている。これは、このメソッドが Interface Builder で追加されたアクションであるということを意味している。続いて sayHello: というメソッドの名前が来る。コロンが1つあるので、引数が1つあるということが分かる。最後に引数。引数の型は id 型、引数の名前は sender 。

1
@end

AppController クラスの宣言の、終わりを表している。

■ AppController.m

1
2
3
4
5
6
7
#import "AppController.h"
  @implementation AppController
  - (IBAction)sayHello:(id)sender
  {
   [textField setStringValue:@"Hello World!"];
  }
  @end

1
#import "AppController.h"

まず #import 文。AppController.h ファイル、つまり AppController クラスのための宣言を読み込んでいる。こちらのファイルでは Cocoa の宣言を読み込んでいないが、AppController.h のほうで読み込んでいるので問題ない。

1
@implementation AppController

AppController クラスの実装の開始を表している。

1
- (IBAction)sayHello:(id)sender

メソッドの実装を表している。sayHello: メソッドの実装になる。

1
{

メソッドの実装の開始を表す中カッコ。メソッドの実装は、この中カッコの内部に書く。

1
[textField setStringValue:@"Hello World!"];

ここからが sayHello: メソッドの中身になる。[] で括られているが、これがメソッドの読み出し。textField オブジェクトのメソッドを呼び出している。Interface Builder で追加したテキストフィールドと、textField とが接続したため、NSTextField というクラスのオブジェクトになる。 setStringValue メソッドは、引数を1つとる。これは、テキストフィールドに設定するための文字列。これに @”Hello World” を指定する。

1
}

メソッドの実装の終わりを表す中カッコ。

1
@end

AppController クラスの実装の終わりを表す。

,