☆Cocoa Foundationクラス(等)勉強室1
文字列;NSString/NSMutableString
「文字列プログラミングガイド」も参照のこと。

Objective-C、厳密にはCocoa Touch上では文字列を必ず@"~"と記述する。 Cなら文字列は"~"だから、違いはどこにあるのだろうか。
それは、@で始まる文字列は「文字列クラスNSStringのポインタ(NSString*)だ」、ということにある。 Cの文字列は(unsigned)char *なので、「異なる」のだ。 @で始まるので、実はこれもコンパイルディレクティブである。

Cocoaでの文字列を扱うクラスは以下の2つである。
NSSring (内部で)変更不能な文字列を管理する。Cのstr="文字列定数"相当
NSMutableString  (内部で)変更可能な文字列を管理する。Cのstr[]={"文字列"}相当
とりあえず簡単な違いを書いたが、実はこれは正確ではない。 NSStringでも文字列の結合等は出来る。 正確には、「NSStringは固定文字列のため、文字列操作の結果を別のオブジェクトに格納する必要があるが、 NSMutableStringは可変文字列なのでその中を直接変更できる」という違いがある。

一方で、NSMutableStringはNSStringと同じに使おうとすると期待通りの動作をしないことが多い (例外を発生して落ちることが非常に多い)。私が不慣れなせいもあるだろうが、 実際にプログラム上で使う時は、「NSMutableStringはローカルの文字列操作時に一時的に使う」のが良さそうである。 操作結果はNSStringに入れてしまう。少なくとも、クラスメンバとして使うのは要注意である。 クラス間のやりとりで「何やら」起こってしまうようである。

先に書いたとおり、@"~"はこのNSString *のインスタンスに代入できる事を意味している。 Cの"~"の文字列もCocoa上でも使えるが、これは単なる固定文字列のアドレスを示しているに過ぎない。 Cの標準関数を使うなら"~"を、Cocoaのクラスライブラリを使うなら@"~"を使うと覚えておけばいいだろう。 ただし、相互変換は可能である。それについては情報編ブログにまとめておいたが、こちらにも書いておく。
NSString*からchar*への変換
    NSString* str = @"AAA";
    char* cp = [str UTF8String];

char*からNSString*への変換
    char* cp = "AAA";
    NSString* str = [NSString stringWithCString: cp encoding:NSUTF8StringEncoding];

NSStringからCFStringRefに変換
    CFStringRef aCFString = (CFStringRef)aNSString;

CFStringRefからNSStringに変換
    NSString *aNSString = (NSString *)aCFString;

追記:
stringWithFormat@"%s"ではCの文字列("~")を受けられるが、UTF8には対応していないので、ASCIIでない文字は化ける。
UTF8対応するにはstringWithCString:encoding:が必要となる。

@"~"は"~"と同様、連続記述による文字列結合が出来る。これは多用するので「出来ることが大切」である。
 "1"  "2"  "3" =  "123"
@"1" @"2" @"3" = @"123"
間にスペースや改行を入れても問題ないことも同じである。 空文字列は@""。Cでもそうだが、NULLは空文字列ではなくアドレス0(NULL==void* 0)なので注意。 だから、ポインタとは比較出来るが、文字列とは比較してはいけない。 よく間違いを見かけるので要注意だ(メーカー提供のCコンパイラのヘッダー内定義で間違っていることすらあった)。

NSStringには非常に多くのメソッドが存在する。 およそ、Cで文字列に対する標準的な操作関数は全て含んでいる感じで、strlen(),strcmp(),strcpy(),strcat(),atoi(),sprintf() 相当はある。
NSString
初期化
メソッド名動作
-(id)initNSStringを初期化して返す
-(id)initWithString:(NSString *)aString文字列格納
-(id)initWithFormat:(NSString *)format ...formatで指定した書式で初期化して返す
+(id)stringWithString:(NSString *)aString文字列をコピーして新しく文字列を作る
+(id)stringWithFormat:(NSString *)format, ...書式を指定して文字列を作る
nilで初期化しようとすると例外が発生するので注意。
「例」
    NSString *str = [[NSString alloc] initWithString:@"string"];
    NSString *str = [[NSString alloc] initWithFormat:@"%d%%です。",100]; // @"100%です。"

基本操作
メソッド名動作
-(NSUInteger)lengthUnicode文字数を得る(バイト数ではない)
-(unichar)characterAtIndex:(NSUInteger)index指定した位置の文字を返す。
UTF-16エンコードによる文字コードである。
-(NSString *)stringByAppendingString:(NSString *)aString文字列結合
-(NSString *)stringByAppendingFormat:(NSString *)format ...フォーマットを含んだ文字列の結合
-(NSArray *)componentsSeparatedByString:(NSString *)separator特定文字列で分割する
-(NSString *)substringToIndex:(NSUInteger)anIndex文字列の先頭から指定文字数(指定位置までではないので注意)の文字列を返す
-(NSString *)substringFromIndex:(NSUInteger)anIndex指定した位置から末尾までの文字列を返す
-(NSString *)substringWithRange:(NSRange)aRange指定した範囲の文字列を返す
-(NSRange)rangeOfString:(NSString *)aString文字列を比較して部分一致した範囲の文字列を返す
「例」
    NSString *str1 = @"abc";
    NSString *str2 = @"とdef";

    NSUInteger length = [str1 length]; // 3文字(バイト数ではない)
    NSString *aString = [str1 stringByAppendingString:str2]; // @"abcとdef"
    NSString *aString = [str1 stringByAppendingFromat:@"は%dです。",256]; // @"abcは現在256です。

文字列変更
メソッド名動作
-(NSString *)lowercaseString小文字にして返す
-(NSString *)uppercaseString各文字を大文字にして返す
-(NSString *)capitalizedString単語の最初の文字を大文字にして返す
-(NSString *)stringByReplacingOccurrencesOfString:(NSString *)target withString:(NSString *)replacement レシーバの指定した文字列を別の文字列で置き換えた新しい文字列を返す
-(NSString *)stringByReplacingCharactersInRange:(NSRange)range
 withString:(NSString *)replacement
レシーバの指定した範囲を文字列で置き換えた新しい文字列を返す
- (NSString *)stringByPaddingToLength:(NSUInteger)newLength withString:(NSString *)padString startingAtIndex:(NSUInteger)padIndex 文字列を指定した範囲で切り取り、代わりの文字列で埋める
「例」
    NSString *str = @"abc,def,ghi";
    NSArray *array = [str componentsSeparatedByString:@","];

比較
メソッド名動作
-(BOOL)isEqualToString:(NSString *)aString文字列内容比較。
アドレス比較で良いのならisEqual:を使う
-(NSComparisonResult)caseInsensitiveCompare:(NSString *)aString 大文字、小文字の区別をせず文字列の比較をして結果を返す
-(NSComparisonResult)compare:(NSString *)aString文字列内容比較
NSComparisonResult(enum値)
NSOrderedAscending左の引数が右より小さい
NSOrderedSame2つは同じ
NSOrderedDescending左の引数が右より大きい
isEqualToStringとはリターン値が異なる
-(NSComparisonResult)compare:(NSString *)aString文字列を比較して結果を返す
-(BOOL)hasPrefix:(NSString *)aStringインスタンス文字列の先頭からがaStringと一致するか返す
-(BOOL)hasSuffix:(NSString *)aStringインスタンス文字列の末尾までがaStringと一致するか返す
「例」
    NSString *stra = @"abc";
    NSString *strb = @"xyz";
    BOOL equala = [stra isEqualToString:@"abc"]; // YES
    BOOL equalb = [strb isEqualToString:@"abc"]; // NO
isEqual:(これはNSObjectのメソッド)による(全一致)比較では、「同じ」=アドレスの一致を意味する。 実内容の比較ではないことに注意しなければならない。 ただし例外として、代入された値も比較する値も@"~"文字列で与える場合は、 Objective-Cが、同じ内容の文字列は同じアドレスが割り付けられる(メモリ節約のため、重複内容の再割り付けはしない)ので問題ない。 しかし、操作後に出来た文字列などとは当然一致しない。

変換
メソッド名動作Cでの相当関数
-(int)intValueint値に変換するatoi
-(float)floatValuefloat値に変換するatof
-(double)doubleValuedouble値に変換するatof
「例」
    NSString *str = @"123456";
    int i = [str intValue];

    NSString *str = @"1.1";
    float f = [str floatValue];

    NSString *str = @"1.23456789";
    double d = [str doubleValue];

ファイル名系
メソッド名動作
-(NSString *)pathExtensionファイルパスから拡張子を取り出す
-(NSString *)lastPathComponentファイルパスから最終構成要素(普通はファイル名+拡張子)を取り出す
-(NSString *)stringByAppendingPathComponent:(NSString *)aStringファイルパスの末尾にファイル名を追加
-(NSString *)stringByAppendingPathExtension:(NSString *)extファイルパスの末尾にファイルの拡張子を追加
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile
 encoding:(NSStringEncoding)enc error:(NSError **)error
ファイルに保存
+(id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error ファイル内容から指定エンコードで文字列を返す。
URL先ファイルには使ってはいけない。
-(id)initWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error: 指定したファイルの内容から指定したエンコードで初期化して返す。
URL先ファイルには使ってはいけない。
-(BOOL)isAbsolutePathフルパスかどうか調べる
-(NSUInteger)completePathIntoString:(NSString **)outputName caseSensitive:(BOOL)flag matchesIntoArray:(NSArray **)outputArray filterTypes:(NSArray *)filterTypes ファイルシステムのパスとしてファイル名が完全か調べる
-(NSArray *)pathComponentsレシーバをパスだと見て、パス区切りで区切られている要素を配列にして返す
-(NSString *)stringByDeletingLastPathComponentファイルパスの最後の部分(/より後)を削除して返す
-(NSString *)stringByDeletingPathExtension文字列から拡張子を取り除く
-(NSString *)stringByResolvingSymlinksInPathシンボリックリンク(エイリアス)のパスをオリジナルファイルのパスにして返す
「例」
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"text.txt"];
    NSString *documentsDirectoryName = [documentDirectory lastPathComponent];
    NSString *fileName = [dataPath lastPathComponent];   // @"text"
    NSString *ext = [dataPath pathExtention];            // @"txt",拡張子がない場合は@""

    NSString *str = [[NSString alloc] initWithContentsOfFile:dataPath encoding:NSUTF8StringEncoding error:nil];
    // URL先ファイルには使ってはいけない

    NSString *str = @"abc";
    [str writeToFile:dataPath atomically:YES encodidng:NSUTF8StringEncoding error:nil];
    
    if([[name pathExtension] isEqualToString:@""]) {
        self.fileName = [name stringByAppendingPathExtension:@"txt"];
    } else {
        self.fileName = name;
    }
    
    [filename stringByExpandingTildeInPath];

URL系
メソッド名動作
-(id)initWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)errorURL先のファイルを読み込む
+(id)stringWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error URL先ファイル内容を指定エンコードで文字列を返す
-(id)initWithContentsOfURL:(NSURL *)url encoding:(NSStringEncoding)enc error:(NSError **)error   指定したURLの内容を指定したエンコードで初期化して返す
-(BOOL)writeToURL:(NSURL *)url atomically:(BOOL)useAuxiliaryFile encoding:(NSStringEncoding)enc error:(NSError **)error URLに書き込む
-(NSString *)stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding 指定したエンコードを使ってURLエンコードの文字列を返す
-(NSString *)stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)encoding URLエンコード文字列を指定したエンコードでNSStringにして返す
「例」
    NSURL *url = [NSURL URLWithString:@"http://konton.ninpou.jp/software/Tiny3D/Samples/dxf/title2.dxf"];
    NSString *str = [[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];
注: 「initWithContentsOfURL:url encoding:NSUTF8StringEncoding はうまく動かない」 という情報がある。
NSURLDownload の後に 「initWithContentsOfURL:url encoding:NSASCIIStringEncoding」を使うと「何とかなる」らしい。

エンコード系(C文字列との変換を含む)
メソッド名動作
-(const char *)cStringUsingEncoding:(NSStringEncoding)encoding指定エンコードしたC文字列を返す
NSStringEncoding(NSUInteger値)
NSASCIIStringEncoding 7bit ASCII文字
NSJapaneseEUCStringEncoding 8bit EUC 日本語文字
NSUTF8StringEncoding UTF-8エンコード(ASCIIベースの伝送や保存用)
NSShiftJISStringEncoding 8bit SHIFT-JIS日本語文字
NSUnicodeStringEncoding 文字列オブジェクトのための規範的なユニコード符号化
NSISO2022JPStringEncoding ISO 2022 e-mail用日本語エンコード
NSUTF16StringEncoding =NSUnicodeStringEncoding
NSUTF32StringEncoding UTF-32エンコード
-(BOOL)getCString:(char *)buffer maxLength:(NSUInteger)maxBufferCount encoding:(NSStringEncoding)encoding 指定したエンコードのC文字列をバッファに返す
-(id)initWithCString:(const char *)nullTerminatedCString encoding:(NSStringEncoding)encoding C文字列から指定したエンコードで初期化して返す
+(id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc C文字列から指定したエンコードで文字列を作る
+(id)stringWithCharacters:(const unichar *)chars length:(NSUInteger)length 指定したC文字列から指定した長さの文字列を作る
-(id)initWithBytes:(const void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding 指定したバイト列と長さとエンコードでNSStringを作る
-(id)initWithBytesNoCopy:(void *)bytes length:(NSUInteger)length encoding:(NSStringEncoding)encoding freeWhenDone:(BOOL)flag バイト列からエンコードを指定してNSStringを返す
-(id)initWithUTF8String:(const char *)bytesC文字列をUTF8 NSString文字列に変換して返す
+(id)stringWithUTF8String:(const char *)bytesC文字列をNSStringUTF8文字列に変換して返す
-(const char *)UTF8StringNSString UTF8文字列をC文字列にして返す

UIKitを使えるようにしたときには、(カテゴリ)により、 以下のメソッドが追加される。
NSString UIKit Additions
メソッド名動作
-(CGSize)drawAtPoint:(CGPoint)point withFont:(UIFont *)font 指定されたフォントを使っている現在のグラフィックス前後関係で、
指定された点からの一直線上に文字列を描画する
-(CGSize)drawAtPoint:(CGPoint)point forWidth:(CGFloat)width
 withFont:(UIFont *)font
lineBreakMode:(UILineBreakMode)lineBreakMode
fontSize:(CGFloat)fontSize lineBreakMode:(UILineBreakMode)lineBreakMode
 baselineAdjustment:(UIBaselineAdjustment)baselineAdjustment
minFontSize:(CGFloat)minFontSize actualFontSize:(CGFloat *)actualFontSize
 lineBreakMode:(UILineBreakMode)lineBreakMode
 baselineAdjustment:(UIBaselineAdjustment)baselineAdjustment
指定されたフォントと属性を使っている現在のグラフィックス前後関係で、
指定された点からの一直線上に文字列を描画する
UILineBreakMode付き
UIBaselineAdjustment等付き
 
必要に応じてフォント特質ができるだけ多く反映されるようにする
 
 
-(CGSize)drawInRect:(CGRect)rect withFont:(UIFont *)font
 
なし
lineBreakMode:(UILineBreakMode)lineBreakMode
lineBreakMode:(UILineBreakMode)lineBreakMode alignment:(UITextAlignment)alignment
指定された境界長方形とフォントを使っている現在のグラフィックス
前後関係で、文字列を描く
UILineBreakMode付き
アライメント付き
-(CGSize)sizeWithFont:(UIFont *)font
なし
constrainedToSize:(CGSize)size
constrainedToSize:(CGSize)size lineBreakMode:(UILineBreakMode)lineBreakMode
forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
minFontSize:(CGFloat)minFontSize actualFontSize:(CGFloat *)actualFontSize
 forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
描画したときの文字列サイズを返す。
ただし、情報編ブログにも書いたとおり、正しくないことがある。
指定されたフォントで1直線上に描画したとき
指定サイズを強制する
指定制約時
1直線上で、指定フォントと属性
1直線上で、可変的なフォントサイズを含む指定制約時
 


NSMutableStringには上記に加え以下のメソッドが追加される (ここも参照)。
NSMutableString
メソッド名動作
-(void)appendString:(NSString *)aString文字列結合
-(void)appendFormat:(NSString *)format ...フォーマットを含んだ文字列の結合
「例」
    NSMutableString *str = [[NSMutableString alloc] initWithString:@"abc"];
    [str appendString:@"とdef"]; // @"abcdef"
    
    int i = 256;
    [str appendFromat:@"%dです。",i];   // @"256です。"
このように、NSStringは固定文字列のため、文字列操作の結果を別のオブジェクトに格納する必要があるが、 NSMutableStringは可変文字列なので「中に直接変更結果を格納できる」という違いがある。

なお、NSStringとの重要な相違として、@"~"は直接代入できない点がある。
NSString *str=@"string";
は正しいが、
NSMutableString *str=@"string";
は正しくない(コンパイラが警告を出す)。 @"~"はNSString*型であることに注意。

配列;NSArray/NSMutableArray
Cocoa Touchの配列クラスはCの配列とだいぶ異なっている。

Cの配列は宣言時に大きさを指定=固定化し、メモリ上に連続で配置する。 その内容の代入は宣言時にも出来るが、後からユーザーが行うことも出来る。 sizeof()で判るのはあくまで宣言した配列のバイト数であり、実際使われている要素数ではない。、 要素番号の管理はユーザーに任されており、確保した領域外を指してバグを生んだり、逆にそれを技として使ったりも出来た。 また、それはポインターにも代入することが出来た。配列名は実質メモリアドレスに相当した。

Cocoa Touchの用意するクラスとしての「配列」には、固定型のNSArrayと可変型のNSMutableArrayがあり、 固定型のNSArrayは、 である。一方、可変型のNSMutableArrayでは、 となる。
両方に共通するのは、
  1. 最初に大きさだけで確保しておくことが出来ない
  2. 要素番号は厳密に管理されるので、0~要素数-1の範囲を超えることが出来ない
  3. 格納できるのはクラスインスタンスだけ
  4. 配列名をポインタには代入できない
  5. 要素内容がメモリ上で連続していない(元々要素に代入されるのはid=ポインタであり実体は別の所にあるので、実体には連続性がない)
である。C配列との違いはよく理解しておかなければならない。

Cでは可変要素数の配列は直接作ることができないので、malloc()を使ってメモリ確保していた。 NSMutableArrayは要素数を追加できる点ではこれに違いが「要素数を一気に固定できない」ということでは malloc()とは違う。具体的には、「途中を飛ばして値を代入することが出来ない」のである。 Cならp=malloc(100);でp[50]とか途中にいきなりアクセスできるが、NSMutableArrayでは0~50までの要素を (仮内容であっても)確定しない限り50番目の要素にはアクセスできない。
Objective-CはCの構文・関数が全て使えるのでいざとなればそちらで作り上げればいいのだが、 「クラスを使っては、最初からランダムアクセス可能な固定要素数配列は作れない」ということは覚えておくべきだろう。 (C++には言語としてmalloc()によらない可変要素数配列を作る構文がある。)

先にも書いたとおり、Cocoa Touchの用意する配列の基本的なクラスは以下の2つである。
NSArray 変更不能な配列を管理する
NSMutableArray 変更可能な配列を管理する
配列の要素はクラスインスタンスに限られる。それはid型のみ格納可能だということであり、 intなどの通常変数型も構造体なども「直接は」格納できない。
(idは実はクラスインスタンスへのポインターなので、「ポインターのみ格納できる」とも言えるが、 Cの通常のポインターは直接代入できない。) これらは、後述のNSNumberやNSValueを使ってクラスに変換して格納する。 ここが「型]で格納する内容を即時に変化できたCの配列との大きな違いである。
逆に、これは要素として別のNSArrayやNSDictionaryを入れることができることを意味する。 クラスなら何でも良いからである。 これにより、階層構造を持った配列を実現できる。

要素番号が0から要素数-1なのはCと同じ。Objective-Cでは要素番号のことを「インデックス」と言うらしい。 要素番号はint=NSUInteger型(これはクラスではない)で指定する。

配列の基本的な生成は以下のように行う。arrayWithObjects:が初期化のためのメソッドである。
NSArray *array = [NSArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil];
//                                           0       1       2       3     要素数=4
配列の終端はnilにする。 要素数はオブジェクト自体が覚えてくれているので数えておく必要がない。 要素番号は数範囲を超える指定をしてもコンパイル時にはエラーは出ないが、 実行時にエラーが発生する。

ということで、そのメソッド一覧。
NSArray
メソッド名動作
-(id)initWithObjects:(id)firstObj, ...要素を列挙して配列を作る
-(id)initWithArray:(NSArray *)anArray別配列から配列を作る
+(id)array空の配列を作る
+(id)arrayWithObject:(id)anObjectオブジェクトで配列を作る
-(id)arrayWithObjects:(id)firstObj, ...要素を列挙して配列を作る
+(id)arrayWithArray:(NSArray *)anArray別配列から配列を作る
-(NSUInteger)count要素数を得る
-(id)objectAtIndex:(NSUInteger)index指定要素番号の要素の取り出し
-(id)lastObject最後の要素の取り出し
-(NSArray *)subarrayWithRange:(NSRange)range指定範囲の要素の取り出し
-(NSUInteger)indexOfObject:(id)anObject指定要素を持つ要素番号を取得
(BOOL)containsObject:(id)anObject指定要素を持つ要素があるかどうかを判定
-(BOOL)isEqualToArray:(NSArray *)otherArray配列を比較
-(NSEnumerator *)objectEnumerator正順列挙子を返す
-(NSEnumerator *)reverseObjectEnumerator逆順列挙子を返す
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag ファイルに内容を保存する
-(id)initWithContentsOfFile:(NSString *)aPath ファイル内容から配列を作る
-(id)initWithContentsOfURL:(NSURL *)aURLURLの内容で配列を作る
+(id)arrayWithContentsOfFile:(NSString *)aPat ファイル内容から配列を作る
+(id)arrayWithContentsOfURL:(NSURL *)aURLURLの中身で配列を作る
-(void)getObjects:(id *)aBuffer range:(NSRange)aRange指定した範囲の配列をaBufferで示されたバッファにコピーする
-(NSUInteger)indexOfObject:(id)anObject指定した範囲のレシーバの配列に含まれるオブジェクトの番号を返す
-(NSUInteger)indexOfObjectIdenticalTo:(id)anObject
なし
inRange:(NSRange)range

レシーバーの配列中でオブジェクトと同じものを捜して、番号を返す
「同じ」=アドレスが同じであって、実内容が同じではない
指定した範囲のレシーバーの配列中でオブジェクトと同じものを捜して番号を返す
-(void)makeObjectsPerformSelector:(SEL)aSelector
なし
withObject:(id)anObject

配列オブジェクトの各要素に対してメッセージ(aSelector)を送る
配列オブジェクトの要素に対してaSelectorのメッセージを引数付きで送る
-(id)firstObjectCommonWithArray:(NSArray *)otherArrayレシーバの配列から配列を探して、最初のオブジェクトを返す
-(NSArray *)sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))comparator context:(void *)context 配列内の要素を関数で比較して昇順にソートする
-(NSArray *)sortedArrayUsingSelector:(SEL)comparator配列内の要素を比較して、昇順にソートされた配列を返す
-(NSString *)componentsJoinedByString:(NSString *)separator配列の要素が全て文字列の場合、配列の全ての要素をセパレータをいれてつなげる
-(BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flagURLに書き込む
-(NSArray *)pathsMatchingExtensions:(NSArray *)filterTypes要素がファイル名を表す文字列の場合、filterTypesの拡張子を持つものだけを返す
「例」
    NSArray *array = [[NSArray alloc] initWithObjects:@"少ない",@"中程度",@"多い",nil];
    // Cのchar *array[]={"少ない","中程度","多い",NULL};に相当

    NSArray *array2;
    [array2 initWithArray:array];

    NSUInteger count = [array count]); // 上記例では4  ; Cの(sizeof(array)/sizeof(型))に相当
    NSString *str = [array objectAtIndex:1]; // @"def" ; Cのchar *str[~]; s=str[1];に相当
    NSString *str = [array lastObject]; // @"jlk"
    NSArray *array2 = [array subarrayWithRange:NSMakeRange(1,3)];
    objectIndex = [array indexOfObject:@"pqr"]; // ないときはNSNotFound
    BOOL isObjectContain = [array containsObject:@"xyz"];

    NSArray *array3 = [NSArray arrayWithObjects:@"abc", @"def", @"ghi", @"jkl", nil];
    BOOL isArrayEquals = [array isEqualToArray:array3]; // 要素数と各要素をisEqual:で比較した結果で判断される

    NSArray *array = [[NSArray alloc] initWithObjects:@"少ない",@"中程度",@"多い",nil];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"arraydata.xml"];
    [array writeToFile:dataPath atomically:YES];
    // ファイルはプロパティリスト

    NSArray *array;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"arraydata.xml"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if (fileExistsAtPath:dataPath]) {
        array = [[NSArray alloc] initWithContentsOfFile:dataPath];
    }
    // ファイルはプロパティリスト
    // URL先ファイルには使ってはいけない

    NSArray *array = [[NSArray alloc] initWithObjects:@"少ない",@"中程度",@"多い",nil];
    NSLog(@"array:%@",[array description]);

NSArrayでは、初期化や読み込みによっていったん確定された内容を変更することは出来ない。 当然要素数も変化しない。 一方、可変配列NSMutableArrayではそれが可能になる。 空の可変配列の生成は以下のように行う。
NSMutableArray *mArray = [NSMutableArray array];
NSMutableArrayは、NSArrayに加え以下のメソッドが追加される(NSArrayを継承しているので、そのメソッドは全て使える)。 NSMutableArray
メソッド名動作
-(void)addObject:(id)anObject配列終端に要素を追加する
-(void)addObjectsFromArray:(NSArray *)otherArray配列終端に配列を追加する
-(void)insertObject:(id)anObject atIndex:(NSUInteger)index配列途中に1要素を追加する
-(void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject 配列途中の要素を置換する
-(void)replaceObjectsInRange:(NSRange)aRange withObjectsFromArray:(NSArray *)otherArray 配列途中の要素を指定配列から置換する
-(void)removeObjectAtIndex:(NSUInteger)index配列の指定要素を削除する
-(void)removeObject:(id)anObject指定内容の1要素を削除する
-(void)removeObjectsInArray:(NSArray *)otherArray配列で指定される指定内容の要素を全削除する
-(void)removeObjectsInRange:(NSRange)aRange指定範囲の要素を削除する
-(void)removeLastObject最後の要素を削除する
-(void)removeAllObjects配列の全要素を削除する
「例」
    [anArray addObject:@"123"];
    [anArray addObjectsFromArray:fArray];
    [anArray insertObject:@"追加" atIndex:0]; // 先頭に追加
    [anArray replaceObjectAtIndex:1 withObject:@"XYZ"];
    [anArray replaceObjectsInRange:NSMakeRange(0,1)
          withObjectsFromArray:[NSArray arrayWithObjects:@"aaa", @"bbb", @"ccc", @"ddd", @"eee", nil]];
    // 使われるのはrange分だけ

    [anArray removeObjectAtIndex:0]; // 先頭を削除
    [anArray removeObject:@"ggg"];
    [anArray removeObjectsInArray:[NSArray arrayWithObjects:@"bbb", @"iii", nil]];
    [anArray removeObjectsInRange:NSMakeRange(2, 3)];
    [anArray removeLastObject];
    [anArray removeAllObjects]; // count=0になる
Cで配列の要素を制御しようとすると、末尾へのデータ追加はともかく、 途中の追加/削除はなかなかに面倒な処理である。それを全て用意してくれているのだから、 これを有効利用しない手はない。

しかし、NSArrayにはCの配列とは決定的に異なる部分がある。 それは「要素がメモリ上で連続している保証がない」ことである。 これは特定のバイト列を作成したいときに問題になる。 JPEGとかExifとか世の中に存在する汎用フォーマットでは決まり切ったバイト列を正確に作り出す必要があるからだ。 構造体でも境界整合やバイトオーダーの問題が発生するので、 こういう場合はmalloc()でメモリを確保してデータを作るしかないのだと思う。 →とか書いてたが、NSData/NSMutableDataがその役を担う。さすがにそれくらい考えてあるか。

「配列:順序だった集合」も参照のこと。


バイナリデータ;NSData/NSMutableData
NSDataは主に、バイナリデータを扱うためのクラスである。 実際にはデータを格納する領域は別のワークに確保され、そこへのポインターがNSDataに代入される。 NSArrayでは各要素内容はメモリ上で連続していないが、 NSDataの内容は連続していることが保証される。これが最大の違いである。 従って、ファイル内容(というか外部入出力全て)を読み込んでどうこうしようというときには必ずこれを使うことになる。

Cocoa Touchでは例によって後からの変更可・不可に2つのクラスに分かれる。
NSData 変更不能なデータを管理する
NSMutableData  変更可能なデータを管理する

NSData
メソッド名動作
-(id)initWithBytesNoCopy:(void *)bytes length:(NSInterger)length
+(id)dataWithBytesNoCopy:(void *)bytes length:(NSInterger)length
bytesからlengthバイトを持ってNSDataオブジェクトを作る。要するにbytesからのメモリ内容操作できるNSDataオブジェクトを作るということ。
bytes=malloc(length以上)である必要がある。 内容はコピーされない。
bytesからのバッファーの管理はユーザーに任されるため、不要になったらfree()する必要がある。
-(id)initWithBytesNoCopy:(void *)bytes length:(NSInterger)length freeWhenDone:flag
+(id)dataWithBytesNoCopy:(void *)bytes length:(NSInterger)length freeWhenDone:flag
bytesからlengthバイトを持ってNSDataオブジェクトを作る。要するにbytesからのメモリ内容操作できるNSDataオブジェクトを作るということ。
bytes=malloc(length以上)である必要がある。 内容はコピーされない。
freeWhenDone:YESにすると、NSDataがreleaseされるときに同時にfree(bytes)も行ってくれる。
-(id)initWithContentsOfFile:(NSString *)path
+(id)dataWithContentsOfFile:(NSString *)path
ファイル内容からデータオブジェクトを作る
-(id)initWithContentsOfURL:(NSURL *)aURL
+(id)dataWithContentsOfURL:(NSURL *)aURL
URL先のページを読み込んでデータオブジェクトを作る
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)flagファイルにデータオブジェクトを書き出す
-(void)getBytes:(void *)buffer length:(NSUInteger)length オブジェクトからbufferへ、lengthバイトをコピーする
-(void)getBytes:(void *)buffer range:(NSRange)range オブジェクトのrange領域分をbufferへをコピーする
-(NSData *)subdataWithRange:(NSRange)rangeデータの一部を使って新しいオブジェクトを作る
-(NSUInteger)lengthデータ長を得る
-(BOOL)isEqualToData:(NSData *)otherDataデータ内容を比較する
「例」
    NSData *myData;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"data.xml"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:dataPath]) {
        myData = [[NSData alloc] initWithContentsOfFile:path];
    }
    // URL先ファイルには使ってはいけない

    NSURL *URL   = [NSURL URLWithString:@"http://~中略/blog/"];
    NSData *data = [NSData dataWithContentsOfURL:URL options:NSMappedRead/*NSUncachedRead*/ error:nil];
    NSLog( [data description] );

    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"data.xml"];
    [myData writeToFile:path atomically:YES];

    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"data.xml"];
    NSData *myData = [[NSData alloc] initWithContentsOfFile:path];
    NSLog(@"myData:%d",[myData length]);

    NSRange range; // location=位置,length=長さ
    range.length = ~;
    range.location = ~;
    NSData *dataForWriteToFile = [writeData subdataWithRange:range];

    NSString *path = [documentsDirectory stringByAppendingPathComponent:@"data.xml"];
    NSData *myData = [[NSData alloc] initWithContentsOfFile:path];
    NSLog(@"myData:%@",[myData description]);

可変データNSMutableDataでは上記に加え以下のメソッドが追加される。 引数の数によって動作が変わるメソッドが多い。
NSMutableData
メソッド名動作
+(id)dataWithCapacity:(NSUInteger)aNumItems変更可能なデータオブジェクトを作る
+(id)dataWithLength:(NSUInteger)length指定した長さを0で埋めた変更可能なデータオブジェクトを作る
-(id)initWithCapacity:(NSUInteger)capacityデータを初期化する
-(id)initWithLength:(NSUInteger)length指定した長さを0で埋めた変更可能なデータを初期化して返す
-(void)increaseLengthBy:(NSUInteger)extraLength指定バイト数大きくする
-(void)setLength:(NSUInteger)length指定バイト数に変更する
-(void *)mutableBytesデータの先頭のポインタを返す
-(void)appendBytes:(const void *)bytes length:(NSUInteger)length末尾にデータをコピーして追加する
-(void)appendData:(NSData *)otherData末尾に他のデータを結合する
-(void)replaceBytesInRange:(NSRange)range
withBytes:(const void *)bytes
withBytes:(const void *)replacementBytes length:(NSUInteger)replacementLength

指定した範囲のデータを置換する
指定した範囲のデータを部分的に置換する
-(void)setData:(NSData *)aDataデータの全てを別のデータで置換する
「例」
    NSString *path = @"~/testWritetext.txt"; //ユーザーディレクトリのトップのtestWritetext.txtというファイルへ
    NSString *str  = @"Mutable data";
    NSMutableData *dat1 = [NSMutableData dataWithCapacity:1];
    [dat1 appendBytes:[str cString] length:[str cStringLength]];

    NSMutableData *dat1 = [NSMutableData dataWithLength:10];
    [dat1 appendBytes:[str cString] length:[str cStringLength]];

    NSMutableData *dat1 = [[[NSMutableData alloc] autorelease] initWithCapacity:1];
    [dat1 appendBytes:[str cString] length:[str cStringLength]];

    NSMutableData *dat1 = [[[NSMutableData alloc] autorelease] initWithLength:10];
    [dat1 appendBytes:[str cString] length:[str cStringLength]];

    [dat1 increaseLengthBy:10];

    [dat1 setLength:25];

    adr = (char *)[dat1 mutableBytes];

    [dat1 appendBytes:[str cString] length:[str cStringLength]];

    NSMutableData *dat2 = [[NSMutableData alloc] autorelease];
    [dat2 appendData:dat1];
    [dat2 appendBytes:[str cString] length:[str cStringLength]];

    unsigned len;
    unsigned char *aBuffer;
    NSString *str2 = @"xxx";
    NSMutableData *dat2 = [NSMutableData dataWithBytes:[str2 cString]length:[str2 cStringLength]];
    len = [dat2 length];
    aBuffer = malloc(len);
    [dat2 getBytes:aBuffer];
    [dat1 replaceBytesInRange:NSMakeRange(1,len) withBytes:aBuffer];

    [dat1 replaceBytesInRange:NSMakeRange(1,len) withBytes:aBuffer length:1];

    [dat2 setData:dat1];


辞書;NSDictionary/NSMutableDictionary
「辞書」というのはC言語としては出てこない概念である。 Objective-Cで言う所の「辞書」とは、配列に近いがアクセスが要素番号ではなく、 「キー」と呼ばれる名前を各要素に付け、それを使って管理する。 「連想配列」などと呼ばれることもあるだ。

Cocoa Touchで辞書を操作する基本的なクラスは以下の2つである。
NSDictionary 変更不能な辞書を管理する
NSMutableDictionary  変更可能な辞書を管理する
キーはNSStringの文字列で付けるが、値については任意のオブジェクトを混在させられる。 辞書内容の設定は、dictionaryWithObjectsAndKeysメソッドで行う。 要素は「内容,@"キー"」の順で格納し、終端は内容部をnilとする(ここにはキーは不要)。
NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:
                         @"miro"                        , @"name"       , // キー name に対する内容が miro
                         [NSNumber numberWithInt:8]     , @"age"        , // キー age  に対する内容が 8
                         @"red"                         , @"color"      , // キー colorに対する内容が red
                         nil];                                            // 終端
または、NSArrayを使って設定する。NSArrayを使って設定した場合、設定後はNSArrayは解放してもかまわない。
NSArray *keys = [NSArray arrayWithObjects: @"name", @"age", @"color", nil]; // キーの配列
NSArray *vals = [NSArray arrayWithObjects: @"futaba",[NSNumber numberWithInt:4],@"white", nil]; // 内容の配列
NSDictionary *dict2 = [NSDictionary dictionaryWithObjects:vals forKeys:keys];
[keys release];
[vals release];
1組のキーと値のことを「エントリ」と呼ぶ。

NSDictionary
メソッド名動作
+(id)dictionaryWithObjectsAndKeys:(id)firstObject , ...キーと値を列挙して辞書を作成する
+(id)dictionaryWithObject:(id)anObject forKey:(id)aKey指定のキーと値から辞書を作成する
+(id)dictionaryWithObjects:(NSArray *)objects forKeys:(NSArray *)keysキー配列と値配列から辞書を設定する
+(id)dictionaryWithDictionary:(NSDictionary *)otherDictionary他の辞書の含むキーと値から辞書を作成する
-(id)initWithObjectsAndKeys:(id)firstObject , ...キーと値を列挙して辞書を作成する
-(id)initWithObjects:(NSArray *)objects forKeys:(NSArray *)keys
なし
count:(NSUInteger)count

オブジェクトとキー配列の内容からエントリを造って、新しく割り当てられた辞書を初期化する
同上、個数指定付き
-(id)initWithDictionary:(NSDictionary *)otherDictionary別辞書にあるキーと値から辞書を作成する
+(id)dictionaryWithContentsOfFile:(NSString *)path
-(id)initWithContentsOfFile:(NSString *)path
ファイル内容から辞書を作成する
-(id)objectForKey:(id)aKeyキーに対応した値を取得する
-(id)valueForKey:(NSString *)keyキーに相当する値を返す
キーの先頭が"@"ではいけない
-(NSArray *)allKeys全キーを配列で取得する
-(NSArray *)allValues全内容を配列で取得する
-(NSArray *)allKeysForObject:(id)anObject辞書内で、指定オブジェクトと一致しているキーを含んでいる新しい配列を返す
-(NSUInteger)count要素数を取得
-(BOOL)isEqualToDictionary:(NSDictionary *)otherDictionary辞書を比較する
-(NSEnumerator *)keyEnumerator辞書内のそれぞれのキーにアクセスするための列挙子を返す
-(NSEnumerator *)objectEnumerator辞書内のそれぞれの値にアクセスするための列挙子を返す
-(NSArray *)keysSortedByValueUsingSelector:(SEL)comparator値によってソートされた辞書キーを配列で返す
-(NSArray *)objectsForKeys:(NSArray *)keys notFoundMarker:(id)anObjectNSArrayとして指定されたキーと一致する辞書から、オブジェクトの配列を返す
-(BOOL)writeToFile:(NSString *)path atomically:(BOOL)flagファイルに辞書を書き出す
-(BOOL)writeToURL:(NSURL *)aURL atomically:(BOOL)flagプロパティリストを指定URLに書き込む
「例」
    NSDictionary *dictionary;
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:"dictionarydata.plist"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:dataPath]) {
        dictionary = [[NSDictionary alloc] initWithContentsOfFile:dataPath];
    }
    // URL先ファイルには使ってはいけない

    NSArray *kArr = [dict1 allKeys];
    NSArray *vArr = [dict1 allValues];
    int dictCount = [dict1 count];

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"dictionary.plist"];
    NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:dataPath error:nil];
    NSLog(@"fileAttributes:%@",[fileAttributes description]);
    unsigned long long fileSize = [fileAttributes fileSize];

    BOOL isDictEquals = [dict1 isEqualToDictionary:dict2];

    id obj1 = [dict1 objectForKey:@"name"];
    指定したキーがないときはnilが返る。
    valueForKeyは使わない方が良い

    NSArray *vals = [[NSArray alloc] initWithObjects:@"Aさん",[NSNumber numberWithInt:50],nil];
    NSArray *keys = [[NSArray alloc] initWithObjects:@"名前",@"年齢",nil];
    NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:vals forKeys:keys];
    [vals release];
    [keys release];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"dictionarydata.plist"];
    [dictionary writeToFile:dataPath atomically:YES];

NSDictionaryでは、初期設定によっていったん確定された内容を変更することは出来ない。 当然要素数も変化しない。 一方、可変配列NSMutableDictionaryではそれが可能になる。そのため、 上記に加え以下のメソッドが追加される。 NSMutableDictionary
メソッド名動作
+(id)dictionary空の辞書を作成する
-(void)setObject:(id)anObject forKey:(id)aKey要素を追加する
-(void)removeObjectForKey:(id)aKey要素を削除する
-(void)removeAllObjects全要素を削除する
-(void)addEntriesFromDictionary:(NSDictionary *)otherDictionary別辞書から要素を追加する
「例」
    NSMutableDictionary *mDict = [NSMutableDictionary dictionary];
    // これはNSDictionaryのメソッドである

    [mDict setObject:@"miro" forKey:@"name"];
    [mDict setObject:[NSNumber numberWithInt:8] forKey:@"age"];
    [mDict setObject:@"red" forKey:@"color"];

    [mDict removeObjectForKey:@"age"]; // 単一キー
    [mDict removeObjectsForKeys:[NSArray arrayWithObjects:@"name", @"age", nil]]; // 複数キーを配列で与える

    [mDict removeAllObjects];

    NSMutableDictionary *mDict2 =
        [NSMutableDictionary
            dictionaryWithObjectsAndKeys:@"futaba", @"name",[NSNumber numberWithInt:4], @"age", @"white", @"color", nil];
    [mDict addEntriesFromDictionary:mDict2];
「辞書: キーと値の集合」も参照のこと。


数値クラス;NSNumber、範囲構造体:NSRange/NSMakeRange/NSIntersectionRange、数値型;NSInteger、NSNumberFormatter
NSNumberは数値を格納するクラスである。数値ならCの標準型でも良さそうなものだが、 型変換(型キャスト)や文字列化、比較などの機能(メソッド)を持たせてある。 配列や辞書へ格納する数値にはNSNumberクラスでしか指定できなくしてある。 当然ではあるが、Cにある数値はlong longも含め全てサポートされている。

NSNumber
メソッド名動作
-(id)initWithInt:(int)value int→NSNumber
-(id)initWithFloat:(float)value float→NSNumber
-(id)initWithDouble:(double)value double→NSNumber
-(id)initWithBool:(BOOL)value BOOL→NSNumber
-(int)intValue NSNumber→int
-(float)floatValue NSNumber→float
-(double)doubleValue NSNumber→double
-(BOOL)boolValue NSNumber→BOOL
-(NSDecimal)decimalValue NSNumber→BCD数値
-(NSString *)stringValue 文字列化
-(BOOL)isEqualToNumber:(NSNumber *)aNumber比較
補足:
initWithと型Valueは以下が一般的な記述となる。

-(id)initWith型名:(型)値
型名=Char,Int,Integer,Long,LongLong,Short,Float,Doubleの他、
Unsigned型名も使える(Float/Double以外)。
NSNumber *number = [[NSNumber alloc] initWith型名:数値];

-(型)型名Value
型名=char,int,integer,long,longLong,short,float,doubleの他、
unsigned型名も使える(float/double以外)。
unsignedのときは次の1文字を大文字にする(例:unsignedInt)
型 val = [number 型名Value];
「例」
    NSNumber *number = [[NSNumber alloc] initWithInt:1];
    NSNumber *number = [[NSNumber alloc] initWithFloat:1.1];
    NSNumber *number = [[NSNumber alloc] initWithDouble:1.23456789];
    NSNumber *number = [[NSNumber alloc] initWithBool:YES];

    int i     = [number intValue];
    float f   = [number floatValue];
    double f  = [number idoubleValue];
    BOOL bool = [number boolValue];

    NSDecimal dnum = [number decimalValue];

    NSString *str  = [number stringValue];

    NSNumber *number1 = [[NSNumber alloc] initWithInt:1000];
    NSNumber *number2 = [[NSNumber alloc] initWithInt:1000];
    BOOL equal = [number1 isEqualToNumber:number2]; // YES
    [number1 release];
    [number2 release];

NSRangeは数値で、範囲を作る構造体である。クラスではない。 配列などのアクセス時において、要素番号範囲を指定するときに使われる。 ユーザーがそれを作るときはNSMakeRange()関数を使う。 またNSIntersectionRange()関数は、2つの範囲の重複部分を返す。
// NSMakeRange(開始位置,範囲長)
// 開始位置:0~
// 範囲長:1~
NSRange aRange = NSMakeRange(  0 , 100 ); // 0~99
NSRange bRange = NSMakeRange( 50 , 150 ); // 50~199
NSRange interRange;
interRange = NSIntersectionRange (aRange,bRange); // NSMakeRange(50,50)になる

NSInteger/NSUIntergerは、クラスではなく数値型の別名である。 基本的には=int/unsigned intと認識して構わないが、 定義上、32ビットであることが保証されている。 (CのintはそのCPUの一番扱いやすいビット数と定義されているのでCPUのビット数が変われば変化するかもしれない。)
クラス名表示フォーマット主な用途
符号なし整数NSUInteger%u,%lu配列の要素番号
符号あり整数NSInteger%d,%ldいろいろ

NSNumberFormatterは、数字を特定のフォーマットやローカライズに対応させて出力させるときに使う。
主なフォーマット文字列は以下の通り。詳細はこちら
主なフォーマット文字列内容
#数字1桁
-符号
.小数点位置
,桁区切り(Grouping separator)
00パディング(桁に足りないとき0で埋める)
¤通貨記号
%100を掛けて%表示する
NSNumberFormatter *formatter = [NSNumberFormatter alloc]; 
[NSNumberFormatter setLocale:[NSLocale currentLocale]];
[NSNumberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *number = [NSNumber initWithFloat:3.14];
NSString* numStr = [formatter stringFromNumber:number];
[number release];
ここで重要なのは、setLocale:でロケール=地域を設定することである。 setNumberFormat前にこれをしておかないと、正しい文字列が返らないらしい。 現在設定されているロケールは[NSLocale currentLocale]で取得できるので、そのままsetLocaleに指定する。 日本語の場合はsetLocale:ja_JPらしい。 詳しくはNSLocale参照のこと。

NSNumberFormatter
メソッド名動作
-(void)setLocale:(NSLocale *)localeレシーバーのロケールを設定する
-(NSLocale *)localeレシーバーのロケールを返す
-(void)setNumberStyle:(NSNumberFormatterStyle)styleフォーマット形式を指定する
NSNumberFormatterNoStyle フォーマット指定なし
NSNumberFormatterDecimalStyle 10進数表示
NSNumberFormatterCurrencyStyle 通貨単位による表示;\1000
NSNumberFormatterPercentStyle %表示
NSNumberFormatterScientificStyle 科学形式;桁が大きい時べき乗表示する
NSNumberFormatterSpellOutStyle 文字で表示(「例」23=英語:twenty-three,日本語:二十三)
-(NSNumberFormatterStyle)numberStyleフォーマット形式を返す
- (void)setFormat:(NSString *)aFormatフォーマットを文字列で指定する
- (NSString *)formatフォーマットを文字列で返す
-(NSNumber *)numberFromString:(NSString *)string文字列からNSNumberを返す
-(NSString *)stringFromNumber:(NSNumber *)numberNSNumberから文字列を返す
-(void)setCurrencyCode:(NSString *)stringISO 4217で通貨コードを設定する
円の場合は@"JPY"
-(NSString *)currencyCode通貨コードを得る
-(void)setCurrencySymbol:(NSString *)string通貨記号文字を設定する
円の場合は@"円"
-(NSString *)currencySymbol通貨記号文字を得る
-(void)setZeroSymbol:(NSString *)string0の時の表示を設定する
-(NSString *)zeroSymbol0の時の表示を得る
NSNumberFormatterはスレッドセーフではないらしい。内部に静的ワークを持っているからだと思われる。 マルチスレッドプログラムするときは要注意である。

型変換;NSValue
数値を示すCの型が配列に直接格納できないのでNSNumberでオブジェクト化したように、 Cのそのほかの型、構造体やポインターもオブジェクト化出来る。 というか、しないと配列に格納できない。 これを行うのが NSValueクラスであり、 コンパイラも@encode()というディレクティブをもってそれをサポートする。

これはほぼ決まり切った書き方をする。
    // 変換;オブジェクト化
    NSValue *val = [NSValue value:&構造体実体 withObjCType:@encode(構造体名)];
    // 逆変換;元の構造体を得る
    [val getValue:&構造体アドレス];
    
@encode()というのは、引数となった型名を規則に従ってCの文字列(=char *)に変換するコンパイルディレクティブである。 詳細は知る必要はないが、構造体だけでなく
    @encode(int **)
    @encode(struct FILE)
    @encode(Rectangle)      // typedef された構造体
等の他、int等基本型、enum、クラス名など、全ての型に使える。sizeof()で使える物は全て使えるらしい。

型を頻繁にNSValueに変換する必要がある場合は、変換をNSValueにカテゴリで機能として追加しておくと便利である。
    // CGPointは構造体名とする
    @interface NSValue (CGPoint)
    + (NSValue*)valueWithCGPoint:(CGPoint)point;
    - (CGPoint)cgpointValue;
    @end

    @implementation NSValue (CGPoint)
    + (NSValue*)valueWithCGPoint:(CGPoint)point
    {
      return [NSValue value:&point withObjCType:@encode(CGPoint)];
    }
    - (CGPoint)cgpointValue
    {
      CGPoint point;
      [self getValue:&point];
      return point;
    }
    @end

    使い方(最初の例と同じ)
    CGPoint point;
    NSValue val = [NSValue valueWithCGPoint:point];
    point = [val cgpointValue];
    NSArray *array = [NSArray arrayWithObects:[NSValue valueWithCGPoint:point], nil];

UIKitを使えるようにしたときには、(カテゴリ)により、 以下のメソッドが追加される。
NSValue UIKit Additions
メソッド名動作
+(NSValue *)valueWithCGPoint:(CGPoint)point指定されたCGPoint構造体を含むvalueオブジェクトを作成して、返す
+(NSValue *)valueWithCGRect:(CGRect)rect指定されたCGRect構造体を含むvalueオブジェクトを作成して、返す
+(NSValue *)valueWithCGSize:(CGSize)size指定されたCGSize構造体を含むvalueオブジェクトを作成して、返す
+(NSValue *)valueWithUIEdgeInsets:(UIEdgeInsets)insets指定されたUIEdgeInsets構造体を含むvalueオブジェクトを作成して、返す
+(NSValue *)valueWithCGAffineTransform:( CGAffineTransform )transform指定されたアフィン変換データを含むValueオブジェクトを作成して、返す
-(CGPoint)CGPointValueレシーバーに入っている値のCGPoint構造体を返す
-(CGRect)CGRectValueレシーバーに入っている値のCGRect構造体を返す
-(CGSize)CGSizeValueレシーバーに入っている値のCGSize構造体を返す
-(UIEdgeInsets)UIEdgeInsetsValueレシーバーに入っている値のUIEdgeInsets構造体を返す
-(CGAffineTransform)CGAffineTransformValueレシーバーに入っている値のアフィン変換データ構造体を返す