2012年6月24日日曜日

__has_feature(objc_arc_weak) を使って賢く安全に ARC/Blocks を使う

iOS 6も発表されて、皆さんARCやBlocksをガンガン使用する感じのプログラミングスタイルに変化してきていると思うのですが、そこで問題になってくるのが後方互換性の話です。特にiOS 4。Blocksを使うとなるとどうしても以下の様に非同期で実行されたBlocksの中からViewを書き換えるようなコードを書きたくなるのですが、
__weak MyViewController *__weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
    __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding];
}];
このようなコードを安全に実行するためにはselfを一度__weakな変数に代入して、それをBlocksにキャプチャさせるようにしないと、以下のような理由で安全ではなくなってしまいます。
  • __strongを使うと、対象の変数をキャプチャしているBlocksの実行が終わるまで対象の変数がreleaseされなくなるばかりか、最悪の場合は循環参照が発生してメモリが絶対に開放されなくなってしまいます。
  • __unsafe_unretainedを使うと、Blocksの実行が終わるまでの間に対象の変数がreleaseされてしまうとEXC_BAD_ACCESSでクラッシュします。
しかしながらiOS 4では__weakが使えず、状況に応じて__strongや__unsafe_unretainedでごまかす必要があります。このようなときにiOS 5では__weakを使い、iOS 4やno-ARCなプロジェクトではそれなりに適切な何かを使って実装するような仕組みが欲しくなります。

そこで便利に使えるのが__has_feature(objc_arc_weak)__has_feature(objc_arc)マクロです。こいつらを使うと簡単に現在のビルド環境・ターゲット環境がARCを導入しているか、weakは使用可能か、を判断できます。たとえば私は以下の様なマクロを組んで、
// ARC & memory management
// Use these prefixes to be compatible with ARC on iOS 5/ ARC on iOS 4.X / non-ARC
// 
#if __has_feature(objc_arc_weak) // iOS 5 or above
#define __my_block_weak        __weak
#define __my_block_weak_unsafe __weak
#elif __has_feature(objc_arc)    // iOS 4.X
#define __my_block_weak        __strong
#define __my_block_weak_unsafe __unsafe_unretained
#else                            // iOS 3.X or non-ARC projects
#define __my_block_weak        __strong
#define __my_block_weak_unsafe __block
#endif
こんな感じのコードにしてます。
__my_block_weak MyViewController *__weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){
    __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding];
}];
こうするとどの環境でも(比較的)安全にblocks内部でselfを触ることができるという寸法です。

ここで気になるのが__has_feature(objc_arc_weak)の判定条件です。個人的にこいつはiOS SDK 5.0以上を使っていたらターゲット環境がiOS 4とかiOS 3とかでも無条件で1を返してしまって使えないんじゃないのかと危惧していたのですが、なんとIPHONEOS_DEPLOYMENT_TARGETの値を見てきちんと値が変化する仕組みになっています!なので例えばSDKは最新だけれどiOS 3もサポートしたいみたいなプロジェクトでは__has_feature(objc_arc)の値が自動的に0になって良い感じに分岐してくれるというわけです。安心して使いまくりましょう!

2012年5月5日土曜日

C# で map とか reduce みたいな楽しいリスト操作をしたい

プログラミング言語がロジックを組むのに向いているかいないかを判断するときの基準に、私はよく「文字列の操作が優れているか否か」「配列・辞書・集合の操作が優れているか否か」を評価点に挙げます。文字列や配列、辞書、集合の操作はほとんどすべてのアプリケーションで必要になり、それらの生産性が高く高速に処理してくれる言語ほど簡単で高速なロジックが組めると思うからです。

そういう意味でObjective-Cを考えると、文字列の操作はまぁまぁ良い(特にUnicode周りがなかなか優れている、正規化もできるし)のですが、配列・辞書・集合の操作がイマイチで、作るの面倒なら操作するのも面倒。さらには良く欲しくなる以下の操作が欠けています。
  • map - 条件式を渡して、もとの集合の各要素に条件式を通した結果を新たな集合として返す。
  • reduce - 条件式を渡して、要素を前から順番に計算して畳み込み、集合から一つの要素にする。
  • any - 一つでも要素が条件式を満たすならtrue, すべての要素が満たさないならfalse
  • all - すべての要素が条件式を満たすならtrue, 一つでも満たさないならfalse
ではMono上のC#ではどうなんだろうということで調べてみたところ、LINQを提供するSystem.Linq名前空間に高度な配列・辞書・集合操作を行うための拡張メソッドが用意されているということがわかりました。

http://docs.go-mono.com/?link=T%3aSystem.Linq.Queryable
http://msdn.microsoft.com/ja-jp/library/system.linq.queryable(v=vs.90).aspx

ということで早速使ってみます。



実行速度が高速なのかどうかはわからないのですが、なかなか面白いです。ラムダ式が使えるのもスマートで素敵ですね。うーん、C#好きになってきたかも。

Mono の System.String と string の違いについて調べてみた

MonoとかC#とか初心者なのでSystem.Stringとstringの違いがよくわからず、気になったので調べてみました。

以下のページのベストアンサーが一番わかり易かったです。
http://okwave.jp/qa/q7225655.html

ちょっと転載します。
string と System.String は型として使われる限り単なる別名なので同じものです。
foo.System.String みたいなものが用意されて foo 名前空間内から使うような場合は異なりますが,特殊例過ぎるのでこれは考えないものとします。
コンパイラは,ソース上の型として string と書かれているものは,System.String とかかれているものとして処理します。
# 正確には "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" という型 (.NET 4 の場合)

これは,SDK 付属の逆アセンブラでコードを見ると,string を使っても System.String を使っても同じコードになることから確認できます。

同じように,int は System.Int32 の別名ですし,bool は System.Boolean の別名です。


使い分けは,特別決まりはありません。
唯一,メソッド名等に使う場合には言語固有の名称を避ける (CLR の名前を使う),というのはあります。
MSDN: 一般的な名前付け規則
http://msdn.microsoft.com/ja-jp/library/ms229045.aspx
具体的には,
・ToInt
よりも,
・ToInt32
の方が好ましい,ということです。
# 上側の名前付け規則だと,Visual Basic においては ToInteger であるべき,となる。


私は,static メソッドの呼び出しに使う場合は CLR の名前を使い,それ以外では言語固有の名称を使っています。
書籍によっては,常に CLR の名前を使うべき,という物もあったりします。
# が, for (Int32 i = 0; ……みたいなコードはほとんど見ません。

自分なりの物でいいので,統一したルールを用意しておくのがよいでしょう。
# 常に言語固有の名称でもよいと思います。

そもそも.NET Framework (とMono) は実装言語に依存しないような作りになっている (CLRというやつ?) のでこのような仕組みになっているようです。なるほど。 int, bool, string はC#の型で、 System.Int32, System.Boolean, System.String はCLRの型なわけですな。勉強になりました。