yashiganiの英傑になるまで死ねない日記

週末はマスターバイクでハイラルを走り回ります

Objective-Cが好きになるTips & Hackを勝手に補足

TLで見かけたこのスライド,いいところをついていてちょっとObjective-Cをやったことのある人が,もっと興味を持つきっかけを作るにはすごく良いスライドだと思った.

けど,内容を見た感じ初学者を対象としているわりに,ここで取り上げられているTipsやHackの負の側面に対するフォローが無い. 一応「やるかやらないかはあなた次第」とあるが,なぜというところが抜けているので初学者には判断できないと思う. 特に紹介されている例はあまり行儀の良い方針では無いように感じたので,これを初学者がそっくりそのまま真似をするのはあまりよろしくない. 反響も結構あるようで気になったので,勝手にこのTipsやHackの負の側面(行儀の良くない面)について補足をしておく.

カテゴリメソッドの命名について

このスライドの例では,UIViewframeを直接いじることができるいわゆるユーティリティメソッドを追加している. しかし,一般的にUIViewのようなカテゴリを追加すると影響範囲が広い対象に対して,このように極端に短い名前のメソッドを追加すべきではない. なぜなら,Objective-Cには名前空間が無いため,短い名前のメソッドは必然的に衝突の可能性が高い. また,カテゴリを追加する際には衝突を避けるため3文字のprefixを付けることが推奨されている. カテゴリで追加するメソッド名には以下のようなprefixをつけるのが好ましい.

@interface UIView (SampleCategoryExtension)
- (CGFloat)sce_left;
@end

@implementation (SampleCategoryExtension)
- (CGFloat)sce_left
{
  return CGGetMinX(self.frame);
}
@end

prefixをつけるとsetterを追加するのは難しくなってしまうが,本来そういう用途で使うべきでは無いものなんだと思う. ちなみに,個人的にはUIViewframeを更新するには以下のようなメソッドを追加するのがクールだと思っている(ここから抜粋).

- (void)cs_changeFrameWithBlock:(CGRect (^)(CGRect frame))block
{
    if (block) {
        self.frame = block(self.frame);
    }
}

// Usage
[view cs_changeFrameWithBlock:^(CGRect frame) {
    frame.origin.x = ...
    frame.origin.y = ...
    return frame;
}];

この例だと,記述量はあまり減らないが,blockを使うことによってスコープがひとつ追加されており,UIViewのプロパティを変更する,という処理をそこにまとめることができている. 端々の記述量を減らすより,このような方針をとるほうが結果的に見通しのよいコードが書けると思う. ちなみに,blockやカテゴリを使わず以下のような記述をすることもできる.

view.frame = ({
    CGRect frame = view.frame;
    frame.origin.x = ...
    frame.origin.y = ...
    frame;
});

カテゴリでのメソッドのオーバーライドについて

NSNullを黙らせる方法として,resolveInstanceMethod:をオーバーライドしているが,これは絶対にすべきではない. なぜなら,カテゴリでメソッドをオーバーライドは原則的に禁止されている. 実際には,解決される順が不定ということなのでやってみると動くように見えることもあるが,必ず期待通りに動くとは限らないので絶対に真似しないで欲しい. (動作を安定させる方法として,別名のカテゴリメソッドを追加し,カテゴリのloadでswizzleするという手段もあるが,それはそれで黒魔術的なのであまりオススメはしない)

また,JSONObjectWithData:options:error:の結果に混じったNSNullをどうにかしたいという要求のわりに,解決策の影響範囲が広すぎるのも気になる. この例ならNSDictionary再帰的に走査し,安全な値に変更するという方針をとったほうが影響範囲が少なくて済む. (まさにそれをやってくれるライブラリもある.)

Objective-Cはすさまじく自由度が高く,とてもおもしろい言語である. 特にここで紹介されているカテゴリや,メッセージ解決の仕組みはObjective-Cを書くものなら一度は書いてみたくなるコードだと思う. しかし便利な反面,行儀よく使わないと危険を伴う機能でもあるので,負の面をちゃんと理解したうえで使ってほしい. スライドでも「大事なのは保守性と安全性。どこまでやるかはあなた次第。」とあるので,しっかりとメリット・デメリット見極めて良いコードを書くように心掛けたい.

関連

Objective-Cで行儀のいいコーディングをするためには,以下のエントリが参考になる.

詳解 Objective-C 2.0 第3版

詳解 Objective-C 2.0 第3版