この春はゆるふわ愛されObjective-Cでキメちゃおう☆
iPhoneアプリで一儲けしたくなっちゃったからObjective-C頑張っちゃうZO☆
とかいうドン引きな冗談はさておき、なんとなく勉強し始めたObjective-Cについての徒然なるまとめです、真面目です。C++がそれなりに出来るObjective-C初学者*1が対象です。体系的に学習してないですし、言語仕様もほとんど見てなく、何が言いたいかというと要ツッコミ。
書き溜め書き足ししていたら、全体的に大変なことになってしまいましたが、ゆるふわなのでご愛嬌。実は、一番ためになるのはリンク部分かもしれません、Let [us goTo:最後の方]。
Objective-Cとかいう言語
Objective-Cは、C言語にぐちゃぐちゃプリプロセッサ的なものを追加した珍妙なクソ言語(失礼*2)です。
トラップの宝庫C++とは全く別物だと捉えるのが正解ですが、実はC++も混ぜられる*3とのことで、中二病的に言えば究極混沌言語=アルティメットカオスランゲー…いやもやは何も言うまい*4。
ファイル
C++同様、インターフェースの宣言などを書くヘッダと実装でファイルが別れます。拡張子はヘッダが.h、実装が.mです。
コンパイラディレクティブ
クラスの宣言・実装など、あらゆる所で@
で始まるものを使いますが、これはコンパイラディレクティブと呼ばれるもので、コンパイラへの命令です。イメージとしてはプリプロセッサ的な感じ(?)で、これを書くことで、コンパイラがオブジェクト指向など上手いことしてくれるようになります。
種類はとてもたくさんあるようで…困ったのものです。
型
まず気持ち悪いのが、この型です。CとObjective-C、果てはランタイムによる型が混在します。
例えば、ブーリアン。C99由来(?)のbool型(true/false)、Objective-C公認(?)のBOOL型(YES/NO)、Mac OSのランタイムの(?)Boolean型(true/false)があります。基本的にはBOOL型を用いるのが正解のようです(参考)。
例えば、ヌル。C由来のNULL、Objective-C公認のnil(またはNil?)、NullオブジェクトのNSNullなど困ったものです*5(参考1, 参考2)。
例えば、文字列。char*型の""
、NSString*型の@""
があるようです。アプリ開発ではNSStringを使うのが一般的だと思います。
プリプロセッサ
必殺技#import
があります。一言で言えば#include+#pragma onceのようです。PHPでいうならrequire_onceでしょうか。これは便利!
@import(コンパイラディレクティブ)にしなかった理由は、何かあるのかなぁと疑問に思ったりもしましたが、とりあえず放置*6。
クラス
とりあえずクラスをマスターすれば、プログラムは組めそうなので、少し詳しくまとめます。
宣言(.h側)
@interface ClassName : ParentName { PrivateVariable *v1, *v2; } @property(nonatomic, retain, etc...) PrivateVariable *v1; - (TypeOfReturnValue)instanceMethod: (type)firstArg label:(type)secondArg; + (TypeOfReturnValue)classMethod; @end
特徴をまとめると、
- @interfaceに始まり、@endに終わる。
- 継承はC++のようにアクセス修飾子指定はなく、原則public継承で多重継承はできない。
- @interface行の直後の中括弧内にプライベート変数を記述。
- @propertyは必須ではないが、getter/setterを作る時に便利。
- "-"から始まる行はインスタンスメソッド。"+"で始まる行がクラスメソッド。
- メソッドは全てpublic。
- メソッドの宣言の仕方は
- 括弧で戻り値の型
(TypeOfReturnValue)
- メソッド名とコロン
instanceMethod:
- 引数が無いならコロンではなくセミコロンで終了。
- 括弧で型と引数名
(type)firstArg
- 第二引数以降は特殊で、ラベルを指定できる。ラベル、コロン、括弧で型、引数名で指定
label:(type)secondArg
。引数の区切りはスペース。- ラベルは必須ではないが、指定するのがObjective-C流らしい。
- ラベルの有無でも関数の型が変わる。
- 括弧で戻り値の型
と言った感じです。
実装(.m側)
#import "ClassName.h" @interface ClassName () - (void)privateInstanceMethod; @end @implementation ClassName // getter/setterがこれで生成できる @synthesize v1; // 先ほどのヘッダで宣言したもの - (TypeOfReturnValue)instanceMethod: (type)firstArg label:(type)secondArg { [super instanceMethod] [self privateInstanceMethod] } + (TypeOfReturnValue)classMethod {} // 上で宣言したプライベートメソッド - (void)privateInstanceMethod {} // コンストラクタ - (id)init {} // デストラクタ - (void)dealloc() {} @end
ポイントは以下の通りです。
- 3行目の@interface〜@endの部分でプライベートなメソッドを定義できる。
- enumなども可
- 正式には「カテゴリ」と呼ぶらしい。
- @propertyと@synthesizeのタッグでgetter/setterを宣言できる(参考)。
- 宣言したメソッドは全部実装すべし。
- メソッド呼び出しは
[レシーバ 関数名 引数]
の形式。- Objective-C的には、この「関数名」を「メッセージ」と呼ぶ模様。
- オーバーライドする時、親クラスを呼ぶにはレシーバにsuperを指定
[super 関数名 引数]
- 自分を指すにはself。
- メソッド呼び出しは
- 言語機能としてはコンストラクタ、デストラクタはないが、init/deallocがその役割を担う。
モダンな書き方
ここまで書いておいてなんですが、実は、上記の方法はレガシーな方法です。一番*8モダンな書き方は以下の通りです(詳しくは最後のリンク集を参照)。
ヘッダ
// {}は使わない! @interface ClassName : ParentName // public変数だけ@propertyで宣言(IBOutletなどもちゃんと書ける) @property(...) IBOutlet UIButton *btn1, *btn2; // publicなメソッドだけ書く - (TypeOfReturnValue)instanceMethod: (type)firstArg label:(type)secondArg; + (TypeOfReturnValue)classMethod; @end
実装
@interface ClassName () // カテゴリ内にprivateメソッドやprivateなenumを定義 - (void)hoge; // ここで@propertyでprivateインスタンス変数を作るのが従来のベストプラクティス //@property(...) NSString *huga; @end @implementation ClassName // この{}内にprivateインスタンス変数を書くのが一番モダンな方法 { NSString *huga; } // publicなものは@synthesizeで、アンダースコア付き変数で=する(慣例)。 // 変数には self.btn1 か _btn1 でアクセス可能。 @synthesize btn1 = _btn1, btn2 = _btn2; // 以降は以前と同様 @end
クラスの使い方
色々あります。
// 基本形 ClassName* instance1 = [[ClassName alloc] init] [instance1 release] // idで型指定省略&クラスメソッドnewの利用 id instance2 = [ClassName new] [instance2 release] // その他、色々 id instance3 = [[[ClassName alloc] init] initWith "arg"] id instance4 = [[[ClassName alloc] init] autorelease] id instance5 = [[[[ClassName alloc] init] retain] autorelease] //...
以下、ポイントです。
その他、ポイント
Objective-Cでプログラミングする際に、厄介だけどよく使うのでどうしても外せない点を箇条書きしておきます。各詳細はリンク集にまとめてあります。
リンク集
参考になるページと概要一覧です。下に行くほど最近見たものになっています。
- C++プログラマ(というか自分)のためのObjective-C講座(というかメモ)
- C++との比較でクラスの書き方がかなり網羅されています。
- VoQnのためのObjective Cコードの読み方 · yaakaito/NLTQuickCheck Wiki · GitHub
- 初学中の痒いところがまとめてあります。
- 今朝のObjective-Cの勉強
- 厄介な部分が網羅的にまとまっています。
- IBを使わずに作るiPhoneアプリ作成入門:第3回 : アシアルブログ
- クラスのalloc/dealloc/release/retainの使い方が書かれています。
- iPhoneアプリケーション開発: Objective-Cにおけるメモリ管理
- alloc、releaseなどのメモリ管理についてのトピックス。
- iPhoneアプリケーション開発: Objective-Cのプロパティについて
- @propertyの代表的な属性、nonatomic/assign/retain/copyの使い方。
- iOSアプリ開発のメモリ管理で気をつけること - A Day In The Life
- こちらにも、@propertyの属性について書いてあります。
- assign/retain/copyの違いは参照カウントがどうなるかの違いのようです。
- その他、releaseすべきかどうかのノウハウがまとめてあります。
- 文字列定数でNSString*を初期化した場合はreleaseしなくて良い、など
- こちらにも、@propertyの属性について書いてあります。
- プロパティに対応するインスタンス変数の命名規則について - Awaresoft
- iphone - Declaring private member variables - Stack Overflow
- クラスのプライベート変数のレガシーな定義方法。
- ヘッダの{}は書かなくても良いです。
- objective c - Class variable defined at @implementation rather than @interface? - Stack Overflow
- @implementationの中に変数書くと?→ただのグローバル変数です。
- objective c - Instance variables declared in ObjC implementation file - Stack Overflow
- Xcode4.2以降では、@implementationに{}を書く、新しいプライベート変数の宣言方法が使える模様です。これは便利。
- こちらも参照:http://stackoverflow.com/questions/8853585/add-ivars-in-implementation
- 余談ですが、Objective-C界隈ではInstance Variableをivarと略すらしいです(参考)。
-
- 日本語のObjective-C公式ドキュメント発見!
-
- @propertyと@synthesizeについてのシンプルなまとめ
- @propertyの属性の挙動の違い - nazolabo
- @propertyの全ての属性の詳説。
ようやく少しObjective-Cが書けるような気がしてきました。やったね!
余談
いかにもなタイトルはホッテントリメーカーによるものです*12。
*1:=私
*2:もっとも、歴史的に見れば、最初のリリースはPerlよりも古い言語のようなので、文句は言うまい…いや、やっぱりブツブツ
*4:C++/CLIとどっこいか…と思ったけど、C++/CLIはアンマネッジな部分のラッパーという大義がある。Objective-CはApple公式言語というのが厄介…CarbonならC++でも開発できるようだけれど…。
*5:Objective-C++ならC++由来のnullも混ざるわけで…恐ろしや
*6:確かに#includeを踏襲しているものだから、コンパイラディレクティブというよりは、ただのコンパイラ依存のプリプロセッサという印象の方が強い気もしなくはない
*7:慣例としてinitWith
*8:2013年始時点では多分
*9:でも何か裏事情はありそう
*11:確かに、ヘッダはなるべく最小限の情報にしたいのに、普通の方法ではそうならず、微妙な気分ではあった
*12:123ブクマになるらしいです…ないな