Android-iOS API対応表(2)

[前回](http://qiita.com/naodroid/items/d84a301527cba7900294)の記事が予想より数段読まれているので、
もうちょっと突っ込んだものでかつ頻出しそうなものをまとめてみました。
引き続き初めての方向けです。なれれば不要ですが、なれるまでは便利なものを目指してます。

言語関係
-----------------
|内容(Java用語)|Java|Obj-C|
|:-----------|:------------|:------------|
|クラス取得|obj.getClass()| [obj class]|
|クラス名取得|obj.getClass().getName()| \[\[obj class] description] (※1) |
|デバッグ用文字列 | obj.toString() | \[obj description] (※2) |
|同期 | synchronized (this) | @synchronized(self) |
|try-catch |　try {} catch {} finally | @try {} @catch {} @finally {} (※3) |
|例外投げる | throw new Exception() | @throw \[NSException exceptionNamed:.....] (※3)|

※1: Javaではあまり使わないないですが、Objective-Cではid型が返ってきて何のクラスかわからないときによく使います。
※2: こちらもよくデバッグ時に使います。```NSLog(@"%@", obj);```とすると配列などの場合、中身もわかり便利
※3:Javaでは例外はよく使いますが、Objective-Cではあまり使いません。一般的にエラーはnilを返したり ```NSError *error = nil;    [self doSomething:&error]; ``` とNSErrorの参照を渡してエラーを検出するのがよくある方法です。


文字列関係
-----------------
|内容(Java用語)|Java|Obj-C|
|:-----------|:------------|:------------|
|文字列長 | s1.length | \[s length] (※1) |
|文字列の検索| s1.indexOf(s2) | [s1rangeOfString:s2] |
| 存在するかどうか| if (s1.indexOf(s2) >= 0)  | if ([s2 rangeOfString:s2].location != NSNotFound) |
|始まっている| s1.startsWith(s2) | [s1 hasPrefix:s2] |
|終わっている| s1.endsWith(s2) | [s2 hasSurfix:s2] |
|部分文字列 | s1.substring(start, end) | \[s1 substringWithRange:range ] (※2) |
|一致| s1.equals(s2) | [s1 isEqualToString:s2] |
|String-int変換 | Integer.parseInt(s1) | [s1 intValue] |
|int-String変換 | String.valueOf(i1) | [NSString stringWithFormat:@"%d", i1] |

※1:これに限らないですが、Objective-Cではドット(.)で引数なしメソッドも呼び出せます。つまり、```s1.length``` とも記述可能。どちらにするかはお好みで。
※2: rangeは、```NSRange range = NSRangeMake(start, length);``` で作る。start-endではなく、start-lengthなので注意


時間関係
-------------------------------
|内容(Java用語)|Java|Obj-C|
|:-----------|:------------|:------------|
| 日付 | Date, Calendar | NSDate |
| unix-time | System.currentTimeMillis() | [[NSDate date] timeIntervalSince1970] * 1000 (※1)|
| スリープ | Thread.sleep(1000) | \[NSThread sleepForTimeInterval:1] (※2) |

※1: 秒単位の浮動小数点でかえってくるので、java同等にしたいなら*1000する必要がある。
※2:これ以外でも、Javaは基本ミリ秒単位の64bit整数、Obj-Cは秒単位の浮動小数点なので要注意。



View関係
-------------------------------
|内容 | Android | iOS |
|:-----------|:------------|:------------|
| レイアウト | view.layout(left, top, right, bottom) | view.frame = CGRectMake(x, y, w, h) |
| レイアウトファイル展開 | LayoutInflater.from(context).inflate | [NSBundle mainBundle] loadNibNamed: owner: ] |
| リソース画像読込 | getResource().getDrawable(id) | [UIImage imageNamed:@"name"] |
| 文字列リソース読込 | getResource().getString(id) | NSLocalizedString("name", nil) |
| view追加 | parentView.addView(childView) | [parentView addSubview:childView] |
| view削除 | parentView.removeView(childView) | [childView removeFromSuperview] |



アニメーション
-----------------------

```java:Android
View view = ...;
TranslateAnimation anim = new TranslateAnimation(....);  //移動用アニメーション定義
anim.setDuration(500);   //500msec
view.startAnimation(anim);
```

```objc:iOS
UIView *view = ...;
[UIView animationWithDuration:0.5f    //秒単位なので500と指定すると悲惨なことに
                   animations:^ {
                            view.frame = CGRectMake(...);  //移動先を指定するだけでアニメーション
                   }
];
```

非同期処理
-------------------------------
```java:android
new AsyncTask<Param, Progress, Result>() {
   @Override
   public Result doInBackground(Param... params) {
       //非同期処理。サブスレッドで実行される
       return result;
   }
   @Override
   public void onPostExecute(Result result) {
        //フォアグラウンドに反映。メインスレッドで実行される
    }
}.execute(param);
```

```objc:iOS
//他にもいろいろ方法がありますが、一番楽と思われる例を。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
   //非同期処理。サブスレッドで実行される
   result = ....;
   dispatch_async(dispatch_get_main_queue(), ^ {
         //フォアグラウンドに反映。メインスレッドで実行される
   }
}
```

JSON解析
-----------------------
こういうJSONがあるとすると、

```json:test.json
{
    "name": "Hoge"
    "age" : 18
}
```

こうやって解析します。

```java:Parse.java
JSONObject json = new JSONObject(jsonText);
String name = json.getString("name");
int age = json.getInt("age");
```

```objc:parse
id json = [NSJsonSerialization JSONObjectWithData:jsonData
                                                 options:NSJSONReadingAllowFragments
                                                     error:&error];
//上の場合だと変数jsonの実態はNSDictionary。{}だとDictionary、[]だとArray
NSString *name = json[@"name"];
int age = [json[@"age"] intValue];
```



おまけ
----------------------------------
**Objective-CにJavaと同じようなprivateなメソッドは存在しないです**
.hに書かなければコンパイル時に警告なりエラーなりにはなりますが、サブクラスで同名メソッドが定義されるとおしまいです。
javaと異なりスーパークラスから呼び出した場合でも、サブクラスのオーバーライドされたメソッドが呼び出されてしまいます。

このためprivateメソッドの先頭にクラス名の何文字化を先頭につけて回避するという運用でカバーします。
ただ結構面倒です...ぶっちゃけ...

とはいえprivateメソッドを見分けやすくするのは効果的なので、先頭に pr_ 等を付けることはあります。
(_のみはフレームワークで使われているのでやめたほうがいいです)

普通なら pr_　、サブクラスで同名メソッドが作られる可能性が高そうならクラス名、と分けるといいです。


**Arcなしの勉強をしておくといいです**
最近のiOS開発ではARC(Automatic Reference Counting)を使うのがふつうなので意識しないですが、
たまにこの参照カウンタ回りで変な挙動します（循環参照でリークしたり）。
このため、小さなサンプル程度でいいのでARCを使わずつくり、retain/release/autoreleaseを学ぶと効果的です。

**例外はなぜ使われないの？**
実は例外はメモリリークの温床となります。

```objc:error
@try {
   NSArray *array = ....;
   //いろいろ処理
   if (errorFlag) {
      @throw [NSException ...];
   }
   //ここにARCによりarrayの解放処理が埋め込まれるが、例外発生のため通過しない＝リーク発生！
}
```

コンパイルフラグを指定すればこのパターンでも大丈夫なコードになりますが、処理が重くなるらしいです。

また以前の手動で管理していた時代だと、例外発生時のカウンタ処理は非常に手間でミスが発生しやすかったというのもあります。
こういう経緯もあり、Objective-Cでは例外をあまり使いません。
アプリ続行不能な状態ならともかく、通信エラーやJSON解析エラー等ではNSErrorを使いましょう。