Skip to content
This repository was archived by the owner on Nov 11, 2021. It is now read-only.

VoQnのためのObjective Cコードの読み方

yaakaito edited this page May 22, 2012 · 63 revisions

どのみちObjective-Cを書くハメになるのでとりあえず読み方を書いていく ##「…これ、何???」 Developer Library 行って調べる

##リテラル/トークン

  • Prefix付きで始まるものは クラス
  • NS Prefixで始まるものは 標準クラス
  • VQ Prefixで始まるものは VoQnのためのobjective-Cコードの読み方用のクラス
  • k+Prefix付きで始まるものは 定数
  • 例えば kVQHaskellVersion
  • 大文字で始まるものは Prefixなしのクラスかもしれないもの
  • 小文字で始まるものは 予約語メソッドCの関数

###Objective-Cの予約語 Cの予約語 + @で始まる系

##ちゃんと知りたい 詳解 Objective-C 2.0

###「オゥ、ワタシ、ニホンゴ、ワカリマセン」 Programming in Objective-C

Objective-Cのあるある

まめ知識みたいなものなので、一旦さらっと「へぇ〜そうなんだ〜」くらいで大丈夫なことを列挙しています。

NSObjectってなんですか

Objective-CのクラスはすべてNSObjectから派生する。 とりあえずはNSObjectがあるとallocができる、ないとできない・・・くらいの認識でよい。

何故Prefixが必要なのですか、何故メソッド名があんなにも長いのですか

名前空間などという高機能なものが存在しないからです。

idってなんですか

ざっくりですが、JavaScriptでいうvarだと思ってよいです。プロパティなどの扱いが変わるので、少し気をつける必要があります。

id hoge = @"hoge";

[hoge fuga]ってなんですか

メソッド呼び出しだと思ってよいです。実際にはメッセージパッシングしてるだけなので、定義されていなくともそれっぽいところを呼び出して、死にます。

ちなみに nil でメッセージを送るとnilが帰ってきます。

[nil ahan] // => nil

Booleanがいっぱいあるんですけど・・・?

BOOLを使います。値はtrue/falseではなく、YES/NOです。はいかいいえで答えましょう。true/falseがYES/NOに対応してると思いました?残念!対応していませんでした!(大体の場合は大丈夫っぽいですが、怖いので控えましょう)

BOOL a = YES;
BOOL b = NO;

NULLとnilとNSNullの違いが分かりません

NULLはC言語のNULLです、C言語の関数を呼び出すときはNULLを使います。 nilはObjective-C用のnullです、普段はnilを使います。 NSNullはnilに似たNSNullというオブジェクトです、使い方が限定されますが、例えば XMLパースしたんだけどbodyが無かったときなどに、何も無かったを示す為に使われます。 NSNullとnilは比較してはいけません。 NSNullかどうかを判定するときは以下のようにします。

if([hoge isEqual:[NSNull null]]) {
    // ...
}

文字・・・列・・・?

Objective-Cの文字列定義は @"string" です。**"string"**はC言語の文字列です。

NSString *string = @"string";

コンソールへのログの出し方

NSLogを使います

NSLog(@"loglog");

エントリーポイントどこ・・・?

普通のiOSアプリの場合は、main.m です。

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    [pool release];
    return retVal;
}

グループとディレクトリは対応していません

グループが専門の用語なので後々詳しく解説します。Xcodeのコード管理はXcodeから綺麗に表示するためだけのものだということを覚えておいてください。

心得

Cの劣化だと思って書け

学習用のコードを書くときのベストプラクティス

自分でがんばってビルドしてもいいですが、ほとんどの場合無駄な時間なのでやめましょう。 かといって、毎回アプリケーションを作っていてはそれこそ時間の無駄ですので、まず適当に練習用のプロジェクトを作ります。

  1. Xcode > File > New > Project から新しいプロダクトを作ります。
  2. ここでApplicationを選んでしまうとダルいことになるので、無視してFramework&Libary > Cocoa Touch Static Libraryを選択しましょう。
  3. プロジェクト名を適当に決めて Include Unit Testにチェックを入れます。
  4. プロジェクトができるので、プロジェクト本体は無視して、プロジェクト名Testsのディレクトリを展開します(正確にはグループと呼ばれる)
  5. OCUnitを使ったテストコードができているはずなので、ここで練習します。実行はCmd+Uです。

NSStringを使ってみよう

NSStringをいじって、objective-cの雰囲気を学びましょう。以下のようなメソッドをプロジェクト名+Test.mへ追加してみましょう。

基礎

- (void)testNSString {
    NSString *string = [[NSString alloc] initWithFormat:@"hoge"]; // NSStringのインスタンスを作る
    NSLog(@"%@", string); // > hoge
}

これが基本的な形になります。また似たようなコードが以下になります。

- (void)testNSString2 {
    NSString *string = [NSString stringWithFormat:@"hoge"]; // NSStringのインスタンスを作る
    NSLog(@"%@", string);
}

この二つの違いは、

  • 前者の場合
  1. NSStringというクラスのインスタンスを作る
  2. インスタンスに対してinitWithFormatを呼び出して、その結果を*stringで受け取る
  • 後者の場合
  1. NSStringというクラスのクラスメソッドにこういう文字列を作ってほしいとお願いする
  2. NSStringのクラスメソッドが内部でNSStringというクラスのインスタンスを作る
  3. 内部で作られたNSStringのインスタンスに対してinitWithFormatを呼び出して、その結果をautoreleaseして返す

何が起こっているかというと、参照カウント数が変わってきます。端的な事を言えば、ARCを使っていない場合前者の例はメモリリークします。 Objective-Cのメモリ管理について少しだけ触れておくと、Objective-Cではインスタンスがどこからも参照されなくなったとき(参照カウント(retainCount)が0になったとき)に解放されます。(RunLoopとかの話もあるが、一旦知らなくてOK) メモリ管理を意識する上でよく使われる物が

  1. retain
  • 参照カウントを1つ上げる
  1. release
  • 参照カウントを1つ下げる
  1. autorelease
  • スコープから出た時に参照カウントを1つ下げる (正確な表現ではないですが、一旦そんな感じだと思っておけば大丈夫です)

これらはすべてのインスタンスに対して呼び出すことができ

- (void)testRetain {
    NSString *string = [NSString stringWithFormat:@"hoge"];
    [string retain];
    [string release];
    [string autorelease];
}

のような形で使用することができます。

またこれらは**ARC(Automatic Reference Counting)**を使っている上ではほとんど考えなくともよくなっていますが、すべてのObjective-CのコードがARCに対応に対応はしていないので、読む上で頭の片隅に入れておく必要があります。(ARCはiOSだと5.0以降でのみ有効)

メソッド名の命名規則

長ったらしいメソッド名であることにお気づきでしょうか。Objective-Cではこのようなメソッド名の付け方が推奨されています。 NSStringのものを例になんとなく説明をすると。

[NSString stringWithFormat:@"hoge"];

これは、Prefixを取り除いた、返される型(NSString => string)を作る為にこのNSStringFormatを利用してください、ということがすべてメソッド名で表されています。合理的ですね。

[string componentsSeparatedByString:@"."];

これなんかは、他の言語でいういわゆるsplitなのですが、これはどのように読むかというと、stringを元に、components(つまりなんらかのリスト)を作りたいので、このString(.)を使ってSeparated(分割)してください ということです。ちなみにこれの返り値は、NSArrayです。

雑にまとめると

返ってくるもの+それを作るのに必要な物すべてを利用方法も含めて

という規則で命名を行います。

[NSString string]

つまりこれは何かというと、返してくれるものだけが書かれているので、初期化だけしたやつください、ということです。

逆にこの規則さえ知っている事でメソッド名を推測することができます、例えば文字列の最後に文字列を足して返してほしいなーと思ったら**string(文字列を返す)byAppend(最後に足して)String(その為の文字列を渡す)**という具合です。実際に探してみると見つかりました、よかったですね!

[string stringByAppendingString:@"hoge"]

配列とディクショナリー

Objective-Cでは配列にNSArray、ディクショナリーにNSDictionaryを利用します。それぞれは以下の要領で初期化します。

- (void)testArray {
    
    NSArray *array = [NSArray array];
}

- (void)testDictionary {
    
    NSDictionary *dictionary = [NSDictionary dictionary];
}

NSStringでも説明しましたが、alloc initとしても初期化することができます。このページでは上記の例のような形で統一していきます。

NSArrayとNSDictionaryに値を追加する

それぞれに値を追加してみましょう、おそらくadd~みたいなメソッドがあるはずですね。はい、ないですね。 これもObjective-Cの特徴ではありますが、NSArrayとNSDictionaryは変更ができません変更可能なオブジェクトを作るときはMutableという識別子を付けるのが一般的で、この場合、NSArrayはNSMutableArray、NSDictionaryはNSMutableDictionaryです。

- (void)testMutableArray {
    NSMutableArray *array = [NSMutableArray array];
    [array addObject:[NSNumber numberWithInt:1]];
    NSLog(@"%d", [[array objectAtIndex:0] intValue]); // => 1
}

- (void)testMutableDictionary {
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    [dictionary setObject:@"hoge" forKey:@"fuga"];
    NSLog(@"%@", [dictionary objectForKey:@"fuga"]); // => hoge
}

ここで気をつけなければいけないのは、NSArrayやNSMutableArrayに追加することができるものはオブジェクトだけという点で、1~10を配列にいれたい、みたいなケースではNSNumberというラッパーを利用する必要があります。(NSMutableArrayの方がその例です)

Mutableなものを利用せずにNSArrayやNSDictionaryに値を入れたい場合は、初期化の際に同時にオブジェクトを追加していきます。

NSArray *array = [NSArray arrayWithObjects:@"hoge", @"fuga", @"piyo", nil];
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"hoge", @"hogekey", @"fuga", @"fugakey", nil];

2つに共通して気をつける事は、Objective-Cではリストの終端に必ずnilが必要になることです。またDictionaryでは、例えばJavaScriptの場合

{
  key : value
}

というような書き方をすると思いますが、Objective-Cでは

[NSDictionary dictionaryWithObjectsAndKeys:@"value", @"key", nil];

の順番になります。忘れがちなので覚えておきましょう。またNSDictionaryにdictionaryWithObjectsAndKeys:などで複数の値を同時に追加するとき、途中にnilが入ってはいけないということに注意してください。nilによって終端を判別しているので、nilが来た時点で適用が止まります。

[[[NSDictionary dictionaryWithObjectsAndKeys:@"hoge", @"hogekey", nil, @"nilkey", @"fuga", @"fugakey", nil] allKeys] count]; // => 1

NSNumberについて触れる

NSNumberの話題があったので少し触れておきます。NSNumberは例えばintだったりBOOLだったりをオブジェクトとして扱う為のラッパークラスです。 よく使われるケースとして

  • NSArray,NSDictionaryなどに入れる
  • performSelector:withObject:など、オブジェクトをidで引数に取るところにintやBOOLを渡したい

などが考えられます。 使い方としては、

- (void)testNSNumber {
    
    NSNumber *nsnumberInteger = [NSNumber numberWithInt:10]; // 10という数値をラップしたオブジェクトが作られる
    NSNumber *nsnumberBool = [NSNumber numberWithBool:YES];  // YESという真偽値をラップしたオブジェクトが作られる
    
    [nsnumberInteger intValue]; // intで10が返ってくる
    [nsnumberInteger integerValue]; // NSIntegerで10が返ってくる
    [nsnumberBool boolValue]; // BOOLでYESが返ってくる
}

Objective-Cのクラス

いまかいてる