読者です 読者をやめる 読者になる 読者になる

woshidan's blog

そんなことよりコードにダイブ。

rubyとAndroidを触っている人がObjective-Cを一年以上ぶりに見たときの感想

iOSお遍路 Objective-C iOS

こちらを参考にしながら、Objective-Cを一年以上ぶりに見たら結構面白かったので少しでも覚えられるようにメモをします。

オブジェクトの生成

NSObject *obj = [[NSObject alloc] init];

往時は、くらくらするだけだったんですが、 rubyJavaをある程度いじってみた今見ると、

[NSObject(レシーバ) alloc(メッセージ)] ... (a)
[(a)(レシーバ) init(メッセージ)] ... (b)
NSObject *obj = (b)の戻り値
// NSObjectはポインタ的なもので持っているのでしょうか?

というような感じで、ひたすら左のレシーバに右側のメッセージを順々に畳み掛けていくような感じに見えて面白いです...。

ポインタ表記はCの系列らしいですね。

メソッドの呼び出し

[obj exeWithArg1:hoge arg2:fuga]

これはいま見ても戸惑います。引数が2つ以上ある場合、スペース以外の方法であけてくれると、私の目にはやさしくてありがたいのですが...。

左側がレシーバなのはいいけど、右側が1つの塊に見えにくいんですよね。

参考: http://www.atmarkit.co.jp/ait/articles/0811/10/news108_2.html

列挙型

// [1] 列挙型. ENUMクラスの定義
// 型の中身はなんなのか(第1引数), 列挙型の型名(第2引数)
typedef NS_ENUM(NSInteger, SampleType) {
  SampleTypeHoge = 0, // 列挙するうち最初の値は何か?
  SampleTypeFuga,
  SampleTypePiyo
}

こういう使い方があるみたいです(参考)。

typedef NS_ENUM(NSUInteger, PKSSettingCellType) {
    PKSSettingCellTypeHowToUse,     // 使い方
    PKSSettingCellTypeLicense,      // ライセンス
    PKSSettingCellTypeRequest,      // ご意見
    PKSSettingCellTypeVersion       // アプリバージョン
};

列挙型自体*1になれてきたのか、結構あわてる感じがないですね。

クラス定義

参考: http://www.atmarkit.co.jp/ait/articles/0811/10/news108.html

/
/* クラスの宣言部 */
@interface MyClass : NSObject {
}
@end

/* クラスの実装部 */
@implementation MyClass : NSObject
@end

かならずインタフェースを書くんですね。

そして全てのクラスが暗黙的にObjectを継承しているとかではなくて、ちゃんと継承元を手で記入するのが意外です。

共通する性質をまとめるとかでなく、1対1で.hと.mのファイルを書くというのがすごい、曖昧なイメージでCらしいな、と思ったらCってついてました。

プロパティ

@property (nonatomic, strong) NSString *name; //[2] property
// セッター, ゲッターを自動的に生成

そこは、生成できるんですね。手で書くのかと思いました...(失礼しました)。

メソッド定義

-(id)initWithName:(NSString *)name sampleType:(SampleType)sampleType; // インスタンスメソッドには - をつけて宣言
+(NSString *) getStaticString; // クラスメソッドには + をつけて宣言

-+ってUML的にアクセス制御のことだろうか、という気が一瞬したのですが、インスタンスメソッドとクラスメソッドの定義なんですね。

アクセス制御は、@private @public @protected のように @をつけて書くそうです。

参考: http://www.atmarkit.co.jp/ait/articles/0811/10/news108.html

@private
    int myInt1;

@protected
    int myInt2;

@public
    int myInt3;

定数とクラス変数

static NSString *const constString = @"const"; // [5]クラス定数
static NSString *staticString = @"static"; // [6]クラス変数

ここは案外おとなしいですね、という気持ちでした。

定数を表す修飾子に見える const に * がつくのが不思議です。

@は文字列を変数で使うときにつける、という風に覚えていて、そういうものなのでしょう。

カテゴリ

今見たら割とRubyのモジュールとやりたいことが近そうでした。

どのクラスを拡張するか宣言している分、カテゴリの方が怖くないですね。

まぁ、誤解がありそうなので、実際のコードにあたって慣れようと思います。

無名カテゴリ

外部に公開したくないメソッドやプロパティを置いたりする、privateクラスのようなものに見えます...。

クラス拡張

Rubyオープンクラスとやりたいことが近そうですが、こちらはコンパイル時に実装漏れなどがあると怒ってくれる方、ということでいいのでしょうか...。

クラス拡張とカテゴリについて

quesera2.hatenablog.jp

上の記事を読んでみますと、カテゴリは、実行時に読み込まれることが保証されていないため、それまでの読み込まれたクラスのオブジェクトに影響を与えないように*2、カテゴリの内部にインスタンス変数を持つことができないそうです。

一方、クラス拡張の内容は、コンパイル時に読み込まれるため、クラス拡張でのインスタンス変数の追加により、実行している間にオブジェクトの構造に変化を与えることがありません。なので、クラス拡張はインスタンス変数が追加することができます。

感想

結構見直していて他の言語で読んだこういうことをこう表現してるのかなー、と思うところが多くて面白かったです。

書くのはやはり骨が折れそうな気もしますが、以前よりは読み書きできそうな気がしました。

さて、読まなきゃいけないコードの割合として、今日のところは、Objective-Cは怖い怖いが取れる程度でおしまいにします。

現場からは以上です。

*1:もしかしたらプログラミング自体

*2:たとえば、コンパイル通って最初にクラスAの定義を見てクラスAのオブジェクトを初期化したときにこのオブジェクトはインスタンス変数aとインスタンス変数bを持っていました。そして、後から読み込んだカテゴリEがインスタンス変数cを持っていました。さて、カテゴリEの読み込んだタイミングにあわせて、これまで生成したクラスAのオブジェクトすべてにインスタンス変数cを初期化した状態で足してください、と言われたら困りませんか。メモリ領域を詰めて動かしてるのに、追加分のメモリ領域はどこから持ってくるんですか、と