YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

iOS開発に際して調べたことの個人的メモ

はじめに

こんにちは、Go界のドン・チードルです。最近ちょこちょこiOS開発について調べ物したりちょろちょろいじってたので、忘れないうちにメモしておきます。ちなみに僕は最近になってiOS開発、Objective-Cを触り始めたような素人ですので、間違った理解をしているかもしれません。コメントなどいただければ幸い。

Getting Started

とりあえず公式ドキュメント読んどけばいいだろと思ったので、Appleの公式ドキュメントを読みながら手を動かしました。

これで「TODO listアプリをStoryboardとかを駆使してコードをほとんど書かずにできるよ、すごい!!」みたいなチュートリアルやったんですが、結局何がわかったのかがあいまいでわけわからなくなったので、とりあえずObjective-Cのコードを適当に書いてみることにしました。

Objective-C

最初のチュートリアルでObjective-Cがなんとなーくわかったので、今度はiOSプロジェクトではなく、普通のCLIプロジェクトを作ってみることにしました。 新規プロジェクト作成時に OS X > Application > Command Line Tool としてプロジェクトを作成すると main.m くらいしか無いプロジェクトが作成されてめちゃわかりやすい!!

とりあえず NSLog() で出力できればいいやってことで適当に色々書いて、最低限Objective-Cを書くために必要な文法だけ抑えた。

エントリポイント

  • main.m がエントリポイント
    • @autoreleasepool というプロパティでARC (Automatic Reference Counting) が呼ばれる。このブロックの中に書いておけば自動で参照カウント見て良きに計らってくれて助かる。

  • プリミティブに string やら int やらあるが、iOS開発の時はあんまり使わない風
  • NS*** といういかにもNext Step出身でございますよという拡張型が用意されている
    • NSString (文字列)、 NSInteger (整数)など
    • 拡張型は全部 NSObject というクラスの拡張なので、初期化とかがめっちゃだるい
      • eg. NSString *foo = [NSString stringWithCString:"だるい" encoding:NSUTF8StringEncoding];
    • でもだるすぎるので糖衣構文で @ を先頭につけると簡単に初期化出来て良い
      • eg. NSString *foo = @"だるい";
    • ArrayやDictionaryもある
      • eg1. NSArray *bar = @[ @"spam", @"egg", @"ham" ];
      • eg2. NSDictionary *buz = @{ @"Monty": @"Python", @"age": @100 };
    • Arrayはちゃんと初期化するときに、最後 nil 終わりにしないといけないのに注意
      • eg. NSArray *bar = [NSArray arrayWithObjects:@"spam", @"egg", @"ham", nil];
  • 基本はimmutableなので、mutableな型が欲しかったら NSMutable*** を使う。
  • ARC使うので大体ポインタ型で宣言する

クラス宣言・実装

  • クラスはヘッダファイル *.h と実装ファイル *.m で定義。ファイル名がクラス名。クラス名は大文字始まり。
    • ヘッダファイルの @interface から @end の間にクラスのメンバ等を定義
      • @property でメンバ変数の定義。いろいろなオプションある。
      • @property プロパティで宣言するとアクセサを勝手に定義してくれる。デフォルトはprotected。
      • メソッドの定義は - 始まりならインスタンスメソッド、 + 始まりならクラスメソッド。メソッド名は小文字始まり。
    • 実装は実装ファイルの @implementation から @end の間に
    • 実装ファイルの先頭で @interface クラス名() から @end と書かれてる部分はクラスエクステンション
      • クラスエクステンションの中でメンバを宣言するとprivate的な扱い
  • 継承はヘッダファイルでクラス名の後に : で宣言する。単一継承。
    • @interface ChildClass : ParentClass
  • protocolというJavaのInterface的なもの( @optional によって実装しないでも良いメソッドも定義できるのが異なる) を定義することでまとまった機能を継承できる
    • 宣言は @protocol プロパティ
    • 継承する時は親クラス名の後に <> 内に記述。複数継承する場合はカンマ区切り。
      • @interface ChildClass : ParentClass <FooProtocol, BarProtocol>

メソッド定義・呼び出し

Objective-Cで一番なれなかったのはメソッド呼び出し(メッセージ式)でした。メソッド見ても最初「なんのこっちゃ?」という感じだったので。

  • メソッド定義は次の通り
    • -/+ (戻り値型)メソッド名部分1:(引数1型)引数1 メソッド名部分2:(引数2型)引数2
    • メソッド名は引数を除いた部分を全部つなげたもの
      • メソッド名部分1:メソッド名部分2: がメソッド名(最後のコロンも入る)
      • へんてこりんに見えるけど、英語で書くとなんか自然言語っぽくなる
        • たいていメソッド名は verb:noun/adv/adj:noun/adv/adj... という感じで続く
    • eg1. - (NSInteger)count
      • NSInteger 型を返す count メソッド(引数が無い)
    • eg2. - (Image *)initWithFile:(NSString *)path Title:(NSString *)title Description:(NSString *)desc
      • Image * 型を返す initWithFile:Title:Decription: メソッド(引数は全部 NSString 型の引数 pathtitledesc
  • メソッド呼び出しは [ ... ] で行う(メッセージ式)
    • eg1. [someString count];
    • eg2. [[Image alloc] initWithFile:@"hoge.jpg" Title:@"great pic" Description:@"flower"];

UI作成

StoryboardやらInterface Builderやらを使えばポトペタで楽ちんなんだろうけど、いろいろ隠蔽されまくるので、最初はなるべくコードで書きながら理解した。このへんのドキュメントを読むとわりと理解できた。

ViewとかViewControllerとか

まあMVCやってますよという話

  • Viewはデータを表示する箱
    • ButtonもWebViewもCanvasも全部Viewです
    • Viewの上下関係に気をつけましょう
    • ViewをView Controllerの中にまとめて画面を構成する
  • View Contollerが画面を構成する単位
    • コンテンツを表示するContent View Controller
    • 画面の遷移情報を管理するNavigation Controller
    • データ情報を管理するContainer View Controller
  • iOSではTableを使ったUIが非常に多いのでTableViewControllerっていうのがあるよ
    • まあ普通にViewController定義してTableViewを持ってるのと変わらないけど、はじめからいろいろ定義してあって便利
  • コンポーネント間のやり取りはprotocolとdelegateを使って行う
    • eg1. ViewController内のView(ボタンとか)のコールバックを受け取るためにViewControllerが自分自身をdelegate
    • eg2. 遷移先のViewControllerからの結果を遷移元が受け取るために、遷移先のprotocolを遷移元が実装して、遷移元が自分自身をそのprotocolのdelegate先とする

全部コードで書く場合に意識すること

  • ViewControllerの loadViewviewDidLoad 内で初期化
  • initWithNibName とか用なしなので自分で init*** を書く
  • イベントはことごとく次のようなメソッドを定義して受け取る
    • - (IBAction)didSomething:(id)sender

snippet

  • initWith***
- (id)initWithTitle:(NSString *)title
{
    self = [super init];
    if (self) {
        self.title = title;
    }
    return self;
}
  • AppDelegate 初期化
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    Hoge *hoge = [[Hoge alloc] initWithString:@"hoge"];
    FooViewController *foo = [[FooViewController alloc] initWithHoge:hoge];
    UINavigationController *nav = [[UINavigationController alloc]
                                   initWithRootViewController:foo];
    self.window.rootViewController = nav;
    
    [self.window makeKeyAndVisible];
    return YES;
}

アプリケーションのルートとNavigation Controllerのルートは別だっていうのは、1回アプリ作っちゃえばわかるんだけど、最初はRootViewControllerって言葉が2回出てきてすごく混乱した。

ところで

はてなブログObjective-Cのsyntax highlight用キーワード調べるのに苦労した。 objc でいいんですね。