連載
» 2008年12月15日 00時00分 公開

Cocoaの素、Objective-Cを知ろう(4):変数のデータ型や文字列の扱いを理解しよう (2/3)

[竹下肯己,株式会社 qnote]

ラッパークラス

 オブジェクト指向の言語は、あらゆるデータをオブジェクトとして扱うように設計されています。Foundationに含まれるクラス群のリファレンスを見ても分かりますが、オブジェクト型(id型)で引数を受け取ることを前提に作られているメソッドもたくさんあります。

 ロジックの中で、int型などの基本的な変数を、オブジェクトとして扱わなければならない場面にもしばしば遭遇します。そのようなときには、ラッパークラスと呼ばれるクラスを利用して、値をオブジェクト化することができます。

 Objective-Cでint型などの数値をオブジェクトとして扱いたいときには、NSNumberというラッパークラスを利用します。以下に、NSNumberの利用例を見てみましょう。

#import <Foundation/Foundation.h>
#import <stdio.h>
int main(void) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  //(1)ラッパーオブジェクトの生成
  NSNumber *wrappedInt    = [NSNumber numberWithInt:123];
  NSNumber *wrappedDouble = [NSNumber numberWithDouble:0.333];
  //(2)値の取得
  printf("wrappedInt intValue : %d\n", [wrappedInt intValue]);
  printf("wrappedInt doubleValue : %lf\n", [wrappedInt doubleValue]);
  printf("wrappedDouble doubleValue : %lf\n", [wrappedDouble doubleValue]);
  printf("wrappedDouble stringValue : %s\n", [[wrappedDouble stringValue] UTF8String]);
  [pool drain];
  return 0;
}
main.m

(1)ラッパーオブジェクトの生成

 イニシャライザ、またはインスタンス生成用のメソッドで、NSNumberのオブジェクトを生成します。上記は、インスタンス生成用のメソッドの利用例です。ラップされる変数の型により、「numberWithInt」「numberWithDouble」「numberWithBool」などが用意されています。

 イニシャライザも、やはり変数の型ごとに「initWithInt」や「initWithDouble」などの名称で用意されています。インスタンス生成のための「initWithXxx」(イニシャライザ)と「xxxWithXxx」(インスタンス生成メソッド)という組み合わせは、Objective-Cでは頻繁に目にします。これらの違いについては、後述の文字列クラスのところで説明します(一部、メモリ管理にかかわる処理については別の回で説明します)。

(2)値の取得

 NSNumberのオブジェクトからラップされている値を取り出すときは、取り出したい型によって「intValue」「doubleValue」「stringValue」などのメソッドを利用します。必ずしも、元の値と同じ型で取り出す必要はありません。上記の例では、int型で生成したオブジェクトからdouble型で値を取り出したり、double型で生成したオブジェクトから文字列型で値を取り出したりといった処理を行っています。

文字列を扱う

 実用的なアプリケーションを作成しようとすると、文字列を扱う場面に頻繁に遭遇します。アプリケーションのタイトルやボタンなどの文言、設定画面やウィザードのナビゲーション、ユーザーが入力した文字列の処理など、対話的なアプリケーションでは文字列の処理が欠かせません。

 多くのオブジェクト指向の言語では、文字列を扱うために専用のクラスが用意されています。これはObjective-Cも同様です。文字列クラスがどれだけ便利で強力であるかは、その言語の使い勝手を大きく左右します。

 Objective-Cの最も基本的な文字列クラスは、Foundationに含まれるNSStringです。このクラスは、文字列を扱うための便利な機能が充実した、非常に強力なクラスとなっています。以下に、NSStringの利用例を見てみましょう。

#import <Foundation/Foundation.h>
#import <stdio.h>
int main(void) {
  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  //(1)一番シンプルな文字列の生成
  NSString *str01 = @"あいうえお";
  //(2)値とエンコーディングを指定して生成
  NSString *str02 = [NSString stringWithCString:"かきくけこ" encoding:NSUTF8StringEncoding];
  NSString *str03 = [[NSString alloc] initWithCString:"さしすせそ" encoding:NSUTF8StringEncoding];
  //(3)文字列にパラメータを埋め込んで生成
  NSString *str04 = [NSString stringWithFormat: @"%@ の次は %@ です。", str01 ,str02];
  //(4)文字列の長さ(文字数)を取得
  int len = [str01 length];
  //(5)文字列を連結
  NSString *str05 = [str01 stringByAppendingString:str02];
  //(6)文字列の一部を切り出す
  NSString *str06 = [str01 substringToIndex:3];
  NSString *str07 = [str01 substringFromIndex:3];
  NSString *str08 = [str01 substringWithRange:NSMakeRange(2, 3)];
  //(7)文字列をトリムする
  NSString *str09 = @"  xxx   ";
  NSString *str10 = [str09 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
  //(8)文字列内で、ある文字列を検索
  NSRange searchResult = [str01 rangeOfString:@"うえ"];
  if (searchResult.location == NSNotFound) {
    printf("見つかりません");
  } else {
    printf("Position: %d, Length: %d です。\n",
            searchResult.location, searchResult.length);
  }
  //(9)文字列が、ある文字列で始まっているか、終わっているか
  if ([str01 hasPrefix:@"あいう"]) {
    printf("%s は %s で始まっています。\n",
          [str01 UTF8String], [@"あいう" UTF8String]);
  }
  if ([str02 hasSuffix:@"くけこ"]) {
    printf("%s は %s で終わっています。\n",
          [str02 UTF8String], [@"くけこ" UTF8String]);
  }
  //(10)同じ文字列か
  NSString *str01b = @"あいうえお";
  if ([str01 isEqualToString:str01b]) {
    printf("str01 と str01b は同じ文字列です。\n");
  }
  // 結果の出力
  printf("str01 : %s\n", [str01 UTF8String]);
  printf("str02 : %s\n", [str02 UTF8String]);
  printf("str03 : %s\n", [str03 UTF8String]);
  printf("str04 : %s\n", [str04 UTF8String]);
  printf("len : %d\n", len);
  printf("str05 : %s\n", [str05 UTF8String]);
  printf("str06 : %s\n", [str06 UTF8String]);
  printf("str07 : %s\n", [str07 UTF8String]);
  printf("str08 : %s\n", [str08 UTF8String]);
  printf("str09 : %s\n", [str09 UTF8String]);
  printf("str10 : %s\n", [str10 UTF8String]);
  [pool drain];
  return 0;
}
main.m

文字列の生成

 コード中の(1)〜(3)は、NSStringクラスのインスタンス(つまりObjective-Cの文字列)を生成する方法の一部です。

 (1)は最もシンプルな方法で、「@"xxx"」のように先頭に@を付加して、二重引用符内に表記して文字列を生成しています。このほか、イニシャライザや文字列生成の専用メソッドを利用する方法もあります。

 例えば(2)は、第1引数にC言語の文字列を、第2引数にエンコーディングを指定してインスタンスを生成する方法です。「NSUTF8StringEncoding」はUTF-8エンコーディングを表す定数です。ほかに、「NSShiftJISStringEncoding」「NSJapaneseEUCStringEncoding」などの定数が用意されています(なお、文字列生成時のエンコーディングには、ソースファイル自体のテキストエンコーディングを指定します。この連載では、第2回で説明したとおりUTF-8でソースファイルを作成しています)。

 「initWithCString:encoding:」はイニシャライザなので、alloc(メモリ割り当て)を実行したうえで呼び出します。一方、「stringWithCString:encoding:」は、引数は「initWith〜」と同じですが、allocメソッドによるメモリ割り当てからインスタンスの初期化までを一気に行ってくれる便利なクラスメソッドです。また、このメソッドはメモリ管理上のある手続きも同時に実行してくれるのですが、これについてはメモリ管理の回で詳しく説明したいと思います。

 (3)の「stringWithFormat:」は、C言語のprintfのようにフォーマットを指定し、そこに埋め込むパラメータを第2引数以降にセットして文字列を生成する方法です(第1引数のフォーマットもObjective-Cの文字列である点に注意してください)。これも(2)と同じく、同様の引数を取るイニシャライザとして「initWithFormat:」も用意されています。

文字列の操作

 コード中の(4)〜(9)は、文字列を操作したり検索したりするための便利なメソッド群の一部です。

 (6)の文字列の切り出しメソッドでは、文字列中の位置を表すためにインデックスが利用されています。インデックスは配列の添え字などと同じで0から始まります。つまり文字列の最初の1文字のインデックスは0です。

 「substringToIndex:」メソッドは、文字列の先頭から、指定したインデックスの1つ手前の文字までを切り出します。一方「substringFromIndex:」メソッドは、指定したインデックスの文字から最後までを切り出します。このあたり、ToとFromの解釈に若干の注意が必要です。

 また、「substringWithRange:」メソッドの引数では、文字列の範囲を表すために「NSRange」という構造体を利用しています。構造体はC言語の概念で、意味のある値の集まりをまとめて扱うための仕組みです(オブジェクト指向のクラスの概念の原型ともいえるものです)。NSRangeは、範囲という概念を表現するいくつかの値をまとめて扱うために用意されています。

 「NSMakeRange」はNSRange構造体を生成するための関数で、第1引数に範囲のスタート位置を、第2引数に範囲の長さを指定します。文字列内の範囲を表す場合には、スタート位置はインデックスを表すことになります。

文字列の比較

 コード中の(10)は、2つのオブジェクトが同じ文字列を表しているかどうかを判定するための「isEqualToString:」というメソッドの利用例です。

 クラスオブジェクトの場合、「==」演算子で比較すると、値ではなくインスタンスが同じかどうかを判定することになります。NSStringの場合は、同じ文字列ならばインスタンスも同じになるように内部で制御されているため、「isEqualToString:」メソッドで比較しても「==」演算子で比較しても結果は同じになるのですが、あくまでも文字列としての値を比較するためには「isEqualToString:」メソッドを利用するようにしましょう。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

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

メールマガジン登録

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