このページは「iPhoneプログラミング入門」(工学社刊、ISBN 978-4-7775-1541-7)を読んで、もっと先へ進んでみたいと思った人への説明です。

次へ->
<-前へ
「次の一歩」一覧へ

最も簡単なテーブルの作り方8

再考、セルの作成について

セルの作成法自体はもう覚えた

テーブルの「セル」というオブジェクトの作成自体は、メソッドtableView:cellForRowAtIndexPath: の中で、以下のような処理により行いました。
UITableViewCell *cell = [
	[UITableViewCell alloc]
		initWithStyle:どんなスタイル reuseIdentifier:適当な識別名
	];
この「セル」とは何なのか、もう一度考えてみましょう。

セルを作成するとは、形式と領域を決めること

テーブルにおけるセルとは、
「決まった形式で、指定した文字列を表示できる領域」です。
さて上記の説明に用いた表記をもう一度考えてみましょう。「決まった形式」で、「指定した文字列」です。 つまり、「形式」はセルを作成したときに決まってしまいますが、「文字列」は指定を変えれば、いくらでも変えられるのです。
このことから、セルを作成するとは、
決まった形式で表示するための領域を確保すること
と考えることができます。

同じ領域のセルの「文字列指定」だけを変更=それが「使い回し」

同じ形式で異なる文字列を表示させるセルが欲しい場合、もし,「そのデータをもう使わない」というセルがあれば、わざわざ新しいメモリ領域にセルを作り直さなくてもそのセルの「文字列の指定」だけを変えればよいということになります。
データが複数ページにわたる場合を考えてみましょう。画面上には一度に10個のセルを表示できるとします。
アプリケーション起動時に、「セルを作って文字列を指定する」作業を10回行います。
そして「次の」10件を見たいときには、最初に作った10個のセルの「表示文字列」だけを取り替えます。
パフォーマンスが良いのは明らかですね。これが「使い回し」です。

セルを使い回すため用意された手法

セルを「使い回さない」のは推奨されない

そういうわけで、テーブルのセルは、「使い回す」ことが標準の手法となっています。そして、ちゃんと「使い回すための手法」が用意されています。
使い回したあと、「解放(もう使わないという通知)」はどうするのか。これも、「適当に判断して」と指示します。これが「release」に対する「autorelease」というメソッドです。我々のほうで積極的に「release」を使うことはむしろ推奨されません。セルを使い回すかどうかは、フレームワークがOSと相談しつつ決めることなので、我々が余計な手出しをするとかえってクラッシュする危険があったりするのです。

では、こうしたことを、これから実践していきましょう。

使い回すために識別名をつける

メソッドtableView:cellForRowAtIndexPath:の中で使われているメソッド、initWithStyle:reuseIdentifier: を見てください。「使い古しのセル」を区別するのが、 reuseIdentifierです。今までは、使い回しを考えなかったので、その引数はnilになっていました。今度は識別名を指定します。
識別名はセル1個ずつではなく、そのメソッド内で作成する「同じ書式」のセルには全て同じにします。そして、普通は「同じメソッド内で作成するセルは全部同じ書式にする」と考えてよいでしょう。ですから、reuseIdentifierに指定するのは固定文字列ということになります。

配列theListを定義したのと同じ場所で、固定文字列idengifierを定義します。(灰色のコードは、「そのまんま手をつけない」部分です)
リスト14 固定文字列identifierの定義
NSArray *theList=nil;
NSString *identifier=nil;
メソッドviewDidLoad中で、identifierに値を割り当てます。
リスト15 identifierへ値を割り当て
- (void)viewDidLoad {
  theList=[[NSArray alloc] initWithObjects:@"Red", @"Green", @"Blue",nil];
   identifier=@"MyDefault";
}
最初の定義で NSString *identifier=@"MyDefault"; としてはいけないのかと思うかも知れません。まあ、悪くはないかも知れません。ただ、他のサンプルコードで、メソッドviewDidLoad中で値を割り当てている例が多いので本記事でもそれにならっています。理由を考えるとすれば、ビューの読み込みに成功したら値を割り当てる、ということにしておけば、万が一ビューの読み込みに失敗したときに、余計な仕事をせずにすむ、ということなのかも知れません。

このidentifierを使って、セルを作成するのですが、これは少しずつ考えていきましょう。基本はこうです。
UITableViewCell *cell = [
	[UITableViewCell alloc]
	 initWithStyle:(UITableViewCellStyle)UITableViewCellStyleDefaultz reuseIdentifier:identifier
];
これで、テーブル theTableView に表示するセルには全てMyDefaultという識別名がつくことになります。

識別名でセルを探す

しかし、このようにセルを作る前に、同じ識別名のついたセルがメモリ上に残っていないか照会します。これは、以下のようになります。
UITableViewCell *cell =[
		tableView dequeueReusableCellWithIdentifier: identifier 
	];
メソッドdequeueReusableCellWithIdentifierの「dequeue」は「queue(待機しているデータの列)からはずす(そして、使う)」という意味です。
このメソッドは「tableView」というものに対して使われています。tableViewはメソッドtableView:cellForRowAtIndexPath:の定義において、以下のところで記されている引数です。
tableView:(UITableView *) tableView:cellForRowAtIndexPath:(NSIndexPath *)indexPath{
	.....
アプリケーションが起動すると、この引数tableViewには、プロパティtheTableViewが入ります。ですから、メソッドdequeueReusableCellWithIdentifierは実際には、theTableViewに対して使われます。

使えるセルがなかったら、はじめて新規作成

指定した識別名のセルが見つからなかったら、これまで行ってきたようにセルを作成します。
	if(!cell){
	
		cell=これまでの、セルを作成する処理
}
こちらはcellをポインタで表しません。cellという変数名自体はすでに定義されてしまっているからです。とあとから説明するのは簡単ですが、実際はどっちにすればいいのか悩むところですね。まぁ、場数です。

autoreleaseメソッドを使う

仕上げは、「そのセルを使うか使わないかは適当に判断して」と指定するメソッド「autorelease」です。これは、新規作成されたセルに対して行うので、以下のようになります。
cell=[これまでの、セルを作成する処理 autorelease];
具体的には以下の通りです。
cell=[
       [
        [UITableViewCell alloc]
          initWithStyle:(UITableViewCellStyle)UITableViewCellStyleDefault reuseIdentifier:identifier
       ] autorelease
  ];

まとめ:セルを使い回すための決まった書き方

以上、メソッドtableView:cellForRowAtIndexPath:全文は、リスト16のように書き換わります。
リスト16 メソッドtableView:cellForRowAtIndexPath:全文
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
	
  UITableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:identifier ];

   if(!cell){
	
    cell=[
      [
       [UITableViewCell alloc] initWithStyle:(UITableViewCellStyle)UITableViewCellStyleDefault reuseIdentifier:identifier
      ]  autorelease
    ];
   }
	
   cell.textLabel.text=[theList objectAtIndex:indexPath.row];
   return  cell;

}

固定値をreleaseする

最後に忘れてはいけない作業です。最初に作成した固定値identifierを、deallocメソッド中で、通常のreleaseメソッドで解放します。リスト17の通りです。
リスト17 identifierを解放する
- (void)dealloc {
		
    [theTableView release];
	  [theList release];
	  [identifier release];
    [super dealloc];
}

ファイルを保存したら、エラーなく実行できることを確かめてください。結果は、修正前と全くかわりません。
これで、アプリケーション「FirstTable」は完成です。
お疲れさまでした。「次の一歩」目次に戻ります