HelloWorld

NDAが緩和されたらしいし、日本語ドキュメントも公開されたようなので、iPhone をいじって遊んでいます.

まずは、HelloWorld からなんて思っていじり始めたんですけど、ObjectiveCの基本構文だとか、
基本の処理の流れとか、Interface Builder の使い方とかで所々はまったので、足跡を残しておくことにします.

基本的には、

  • Getting Start Guid
  • iPhone OSプログラミングガイド

を参考にしています

これらのドキュメントの日本語版は、

https://developer.apple.com/jp/iphone/library/japanese.html

から入手できます.
(サイトへのアクセスは、IDP(iPhone Developer Center)への登録が必要になります.

サンプル "MoveMe" について

Getting Start Guid 中で、サンプルとしてあげられている "MoveMe" の解説がありますが、
実際に入手できるものと構成が変わっています.

参考にはなりますが、入手できるソースコードを見比べながら追っていこうとすると、細かな違いに
混乱するかもしれません

基本的な処理の流れ

Getting Start Guid では、main関数内で、以下のようにアプリケーションの Delegate
登録しています.

int retVal = UIApplicationMain(argc, argv, nil, @"MoveMeAppDelegate"); 

これが、サンプルコードでは、次に示すように Delegate の指定自体がありません.

int retVal = UIApplicationMain(argc, argv, nil, nil);

じゃあ、どうやってエントリーとなる Delegate が決定されているのかというと "Info.plist" 内に
記述されている、"Main nib file base name" によって、最初に利用される nib ファイルが決定され
ます.

nib ファイルってのは、Interface Builder(以下、IB) 2.0 のファイルで、「ニブ」と読むらしいです.
また、IB 3.0 では、xibってファイルになって、こちらは「ジブ」と読むらしいです.

"MoveMe" のサンプルでは、"MainWindow" がメインのnibとして登録されているため、UIApplicationMainで
nilが指定されている場合には、"MainWindow.xib" が呼び出されます

MainWindow.xib をダブルクリックして、IBを起動させると MoveMeAppDelegate オブジェクトが登録されている
ことがわかります.

こいつの接続関係を見ると(オブジェクトの上で、右クリック)、"Referencing Outlet" に"Field's Owner"が
指定されていることがわかります.

また、"Field's Owner"のインフォメーションを見てみると、class のタイプが "UIAppication" になって
いることもわかります.

ここから、MainWindow.xib が初期化されたときには、"MoveMeAppDelegate" が呼ばれることがわかります.

UIView と UIViewController について

"MoveMe" では、MainWindowが作成されると、MoveMeAppDelegate の "applicationDidFinishLaunching" が
呼ばれます.

この中では、次のような形で View の作成を行っています.

    UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MoveMeView" bundle:[NSBundle mainBundle]];

MoveMeView は、MainWindow と同様、xib ファイルで記述されていて、上記のような指定でメインのバンドルから対応する xib(nib)ファイルが
読み込まれます.

コードを読むときの注意点としては、"MoveMeView"は、UIView から派生したオブジェクトであって、UIViewController から派生した
オブジェクトでは*ない*点に注意が必要です.

UIViewController は、UIView に対してメッセージを送信するためのコントローラーの役割を行うことになります.

初期化メソッドの後半で、

    UIView *controllersView = [viewController view];

このようなコードが出てくるのは、 UIViewController 内部に読み込まれた UIView を取得しているということです.

View の切り替えについて

"MoveMe" を参考にしながら、最初からプロジェクトを作成して HeloWorld を作ろうと思った場合、同様の手順で
View を作成することになるかと思います

View の xib ファイルは、IBを利用して作るのですが、このときに "Field's Owner" を "UIViewController" として
指定し、View の "Referencing Outlet" に、"Field's Owner" を指定することを忘れてはいけません.

私は、この部分の仕組みがよくわからずに、丸一日わけのわからないデバッグをすることになりました.

    UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MoveMeView" bundle:[NSBundle mainBundle]];

このような形で初期化を行うときに、View "Field's Owner" が UIViewController に適合しないため、例外が
発生していたことに気づかなかったわけです....

プロパティについて

UIViewController の初期化が終了後、 "self.viewController" プロパティに初期化済みの UIViewController を取って
おきます.

    UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MovwMwVieww" bundle:[NSBundle mainBundle]];
    self.viewController = aViewController;
    [aViewController release];

後の処理で、UIViewController を利用するためにこのようにしていますが、この "slef.viewController" をC/C++
いうところの構造体メンバへのアクセスと捉えてはいけません.

ヘッダファイルを見てみると

@interface MoveMeAppDelegate : NSObject <UIApplicationDelegate> {
    UIWindow *window;
	UIViewController *viewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) UIViewController *viewController;

@end

この用に定義されているため、プロパティの基本文法が分かっていないと、「なんかよくわからないけど、メンバ変数 viewController
に対するアクセスを定義しているんだろう」程度に捉えてしまいがちです.

実際には、プロパティ定義によって setter/getter が作成されており、set時には"retain"指定があるのでオブジェクトの
参照カウントを setter が加算しています.

プロパティの挙動が分かっていなかったため、

    UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MovwMwVieww" bundle:[NSBundle mainBundle]];
    viewController = aViewController;
    [aViewController release];

このようなコードを書いてしまったのですが("self."が指定されていない点に注意)、setter を通らず、メンバ変数の viewController に
オブジェクトのポインタをコピーし、さらに retain されていないため、[aViewController release] によって、オブジェクトが
解放されていることに気がつきませんでした.

プロパティを使わないんだったら次のようにするか

    UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MovwMwVieww" bundle:[NSBundle mainBundle]];
    viewController = aViewController;
    // [aViewController release]; // 削除

もしくは、直接

    viewController = [[UIViewController alloc] initWithNibName:@"MovwMwVieww" bundle:[NSBundle mainBundle]];

こんな形にすればよいはずです.

メンバ変数をプロパティとして公開する場合、変数名とプロパティ名を同じにすると、実装定義中に

    @synthesize viewController;

としていすることで、暗黙に setter/getter が作成できるというのが理解できていないと、激しく混乱する部分です.

今回のまとめ

上記点だけ押さえておけば、"MoveMe"のソースコードを読みながら、"HelloWorld"を書いてみるのは、それほど難しく
ありません.

ただし、ObjectiveC の基本文法は、押さえておかないといらぬところではまる可能性が高いので、怪しい部分は
確認しながら作業を進めましょう

https://developer.apple.com/jp/iphone/library/japanese.html

から入手できる、「ObjectiveC 2.0 プログラミング言語」を参照するか、ソフトバンククリエイティブの発行している
「詳細 Objective-C 2.0」を参照するのがよいかと思います.

そして、おそらく瞬間的にしか参照することはないかと思いますが、ビー・エヌ・エヌ新社の
「たのしいCocoaプログラミング(Lepard 対応版)」は、mac os上での開発が右も左も分からないって人に
とっては、とてもよい道しるべになると思います

Interface Builder の必要最低限の使い方は、この本を参照することで理解できると思います.

詳解 Objective-C 2.0

詳解 Objective-C 2.0

たのしいCocoaプログラミング[Leopard対応版]

たのしいCocoaプログラミング[Leopard対応版]