FlashとActionScriptをこよなく愛する私が、つまりはFlashすきすきおっさんがXCodeとObjective-Cについて入門しようとしたときにどうしてもつまずいた点を挙げていきたいと思います。実際にはFlashぜんぜん関係ない記事になっていますが。
FlashやActionScriptと比べてあれがないこれがあるとかいうのはナンセンスなのですが、自分が今学習中の開発スタイルでXCodeだとどうやって開発していったらいいのか、同じようにできるところは同じように、同じようにはできないところは違う方法を探りながらやっています。
とにかくXCodeとObj-Cについて初心者以前であり独学ですので盛大に間違えている可能性があります。
さらにどっちかというと人に説明すると言うよりは 自分がつまずいたところをあとで見直すように、完全に自分向けの個人的な覚え書きのつもりで書いています。
それでもいちおうもしかしたら誰かの役に立てれば幸いですので、わかりにくい箇所や間違っている箇所がありましたらご指摘いただけると嬉しいです。
とにかく理解しにくかったXCodeという開発環境について
Flasherが全員というわけではなく、というかFlashは関係なく私個人の問題なのですが、とにかくXCodeの開発スタイルをものにするのに3年以上かかりました。
Flashと比べてどうこう言うのがナンセンスだというのはよくわかっていますが、Flashはもともとアニメーションやモーショントゥイーンの機能があるお絵かきツールに他ならないので私みたいなお絵かきだいすきおっさんには簡単で使いやすい、とっつきやすいツールでした。
さらに開発と言ってもFlashではお絵かきにアクションを記述する、といった程度の気軽な感じなのでFlashは自分には相性がとてもよかったんですね。
ところがXCodeときたら、Flashと同じような気持ちでXCodeと向かい合うと、 いろいろと腑に落ちないことがたくさん生まれました。
XCodeは開発ツールであり、Smalltalkの影響を強く受けつつC言語にオブジェクト指向の機能を追加する形で生まれたObjective-Cという言語を操り、プロジェクトをコンパイルするためにgccをめっちゃ使いやすくしたシェルがXCodeと言える、というのはわかるんです。
さらに、Obj-Cを使ってiPhone用にアプリを作りやすくしたフレームワークがCocoa Touchで、FlashでいうところのWEBサイトをFlashで作りやすくしたProgressionのようなフレームワークが最初から入ってる状態みたいだ、ていうのも頭では理解できていました。
言葉にして理屈で言うと理解できているんですが、まさに言葉通り”腑に落ちる”、知識を知恵として自分のものにするのに長い年月がかかってしまいました。
その間、何度も何度も繰り返しXCodeを触ってるのに全く使えない自分が本当に嫌になって辛かったです。
このへんは独学の辛いところで、というか全ての独学をしている人に当てはまるわけではないので自分の学習スタイルがまずいだけなんだと思いますが、ものごとを体系立てて学べないので、本当に自分で完全に腑に落ちるまでは一切使えないという自分の能力の問題なのですが、とにかく大変でした。
突破口はカスタムクラス。そしてデリゲート、アウトレット
そんな暗中模索、五里霧中でXCodeを触っている中で、突破口が見えてきました。
コツはカスタムクラスにありました。これは考え方がActionScriptのカスタムクラスと考え方が似ているので、InterfaceBuilderで目に見えている部分とソースコードの部分とを結びつけて考えられるようになるための、まず最初の架け橋となりました。
カスタムクラスさえわかってしまえば、デリゲートやアウトレットに関してはひたすらNSlogをとり続けるという、ActionScripterならお得意のtraceしまくりんぐ殺法と同じ方法でトライアンドエラーを繰り返しながら調べることで、なんとか使えるようにまではなりました。
デリゲートに関しては、現時点での理解度で言うとちょっと使いどころが見つからないのが正直な感想です。
InterfaceBuilderとソースコードとの架け橋としては手っ取り早くて便利なんだけど、この手っ取り早くて便利というのがくせ者で、使うための規約をはっきりさせておかないと私みたいな半端な理解度ではあっというまに肥大化したクラスが出来上がりそうです。しかも委譲されまくりんぐで一体何に委譲されてるのか何の仕事するクラスなのかサッパリわからないクラスにもなりそうです。何をしているクラスなのかはっきりさせるように作ることと、クラスを小さく作ることを練習している私がいま使うのはまずそうです。
きちんとした規約が作れるくらい正しく理解するまではデリゲートはなるべく使わず、カスタムクラスを用いて処理したほうがいいのかなーという印象を受けました。
ただ、入門書ではデリゲートを使った解説がとても多く、ここを自分なりにカスタムクラス側で処理するように書き換えられるくらい言語に精通しているわけではまだないので、入門書でデリゲートが使われるたびに代替方法を探して手が止まってしまうので、とても大変でした。
アウトレットに関しては後日追加。
なんか難しく考えてない?
難しく考えすぎているというのは、人にたまに言われたり自分でもそうなのではないかと悩んだりしました。
習うより慣れよというのに、自分の融通のきかなさには自分でも嫌になったりしました。
ただ、難しく考えているというのは間違いで、むしろその逆です。自分は自分の能力を信用していないので、難しいことはできるだけコンピューターに仕事をさせようとしているだけなのです。難しいことだけに限らず簡単なこともできるだけたくさん、できれば全部、コンピューターが仕事するように、プログラムを書くことくらいしか自分はできないと思っているので、その実践方法を探っています。
自分の能力を過信するよりは先人の知恵を活かしたいと思っています。
あと、ソースコードに自分の理解できない箇所は1行たりとも入れてはいけないとも言われますので、 その考えに沿って本当にただただ自分の理解度が低くそして遅いだけなのだと思います。
しばらくは大変ですが、自分の独学法がこれ以外に知らないのでそういう感じですすめていきたいと思います。
実際アプリとして売る段階までなったときに、自分の知らない動きをするものは売れないですし。
ここまでは概念的な問題というか、よくわかってなかったことをまとめてみましたが、InterfaceBuilderにカスタムクラスを指定できるようになってからどんどんと実践で試すことが出来るようになったので、ここから先はテクニカルな行き詰まりをまとめていきます。
コンストラクターが無い!
Obj-Cにはコンストラクターの機能がないそうなので、NSObjectのinitをオーバーライドしてそこに処理を書いていくことになるそうです。
ちなみにオーバーライドは特別な記述が必要無く、スーパークラスのメソッドを上書きできるみたいです…。どれが親クラスのオーバーライドなのかサブクラス独自のメソッドなのか、ソースコードを一見しただけではわからないので注意が必要ですね。
-(id)init{ if(self = [super init]){ //ここにこのクラスの初期化処理を記述 } return self; }
使うときには
Hoge* huge = [Hoge new];
こうやって使います。newメソッドは
Hoge* huge = [[Hoge alloc] init];
の省略形みたいなので、newしとけばこれでinitが呼ばれます。alloc initするよりも書き方が単純化できるのと、newのほうがFlashのオブジェクト指向っぽいのでnewのほうを気に入って使っています。
追記
newは習慣的に非推奨です http://t.co/h59VRFb9 http://t.co/PFZA2iFZ http://t.co/dbeyB9eV "個人的にXCodeとObj-Cの難しかったところやつまづいたところ | 宇…" http://t.co/aTaH4jcb
— azu (@azu_re) October 4, 2012
newは非推奨と教えて頂きました。ありがとうございます!
定数はどうやるの?
ActionScript3でいうところのconstのやりかた。#defineとかもあるらしいけど自分で気に入ったやり方はこちら。
Pref.h
extern NSString * const _APP_NAME;
Pref.m
NSString * const _APP_NAME = @"ほげほげ祭り";
externについてはまだちょっとよくわかってないです。
public staticな変数や定数がない!?
ActionScript3でいうところのEvent.COMPLETEみたいなやつを、Obj-Cではどうやるのかいろいろ試してみたんですが、今の私のスキルではできないのかそれとも最初から無いのかわかりませんが、とにかくクラスから直接staticな変数や定数を呼び出すことが出来ませんでした。なので今はインスタンスから取得する方法をとっています。何かいいやり方ありましたら教えて頂けると幸いです。
Pref.h
extern NSString * const _APP_NAME; @property (readonly) NSString *APP_NAME;
Pref.m
NSString * const _APP_NAME = @"ほげほげ祭り"; -(NSString*)APP_NAME { return _APP_NAME; }
@propertyを使うとローカル変数とgetter/setterが同じ名前の場合に@synthesizeの記述の必要がない、みたいな事をちらっと聞いたのですが、うまくいく場合とうまくいかない場合があって、その違いがまだ見つけられないので結局こういう場合のgetterは自前で書いています。
使うときは一旦インスタンス化して使うようにしています。
Pref *pref = [[Pref alloc] init]; NSLog(@"アプリ名=%@", pref.APP_NAME);
イベントリスナー
クラス間でやりとりをする場合、
- グローバルな領域で変数や関数を使ってやりとりする
- パブリックメソッドを呼び出す
- コールバックを用意する
- イベントリスナーを使う
ざっくりこういう方法があると思いますが1番目は私のなかではあんまりやっちゃ駄目な方法だと思っています。
ということでだいたいはそのほかの2番から4番までの方法をとることになると思うのですが、完全に偏見ですがObj-cにはイベントリスナーなんてなさそうなイメージを持っていました。
ところがちゃんとありました!完全に偏見ですね。ごめんなさい。
NSNotificationCenter – iPhoneアプリ開発の虎の巻
イベントリスナーを使うとクラスどうしがお互いを知らなくても必要なときに必要なぶんだけやりとりできるのでとても便利です。
ちなみにリンク先のサイトはリファレンスが充実していてしかもわかりやすく、リファレンス命な私は一日100回以上アクセスしています。とてもお世話になっています。
シングルトン
どこで使われても必ずインスタンスが一つしか作成されないことを保証されているシングルトンはたいへん便利なので、Obj-Cでもよく使います。
DataManager.h
@interface DataManager : NSObject +(DataManager*)sharedManager; +(id)allocWithZone:(NSZone*)zone; @property NSDate *date; @end
DataManager.m
@implementation DataManager static DataManager* _dataManager; +(DataManager*)sharedManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _dataManager = [DataManager new]; }); return _dataManager; } +(id)allocWithZone:(NSZone*)zone { @synchronized(self){ if(_dataManager == nil){ _dataManager = [super allocWithZone:zone]; return _dataManager; } } return nil; } - (id)copyWithZone:(NSZone*)zone { return self; } @end
どうも今私が使っているXCode4.4ではARCという機能が働いていて、メモリ管理なんかのやり方が違ってきているようでもともとARCがないときのコードでは書き方が違うようです。
この記事ではARCが働いているほうのシングルトンの書き方を参考にしました。
何をしているコードなのか、自分でわからない箇所は1行でも混ぜてはいけないというルールに従いつつ、さっそくコードリーディング。
どうもdispatch_onceがアプリの動作中に一回しか実行されない事を利用して、インスタンスを生成しているみたいなのですが、引数に&や^など記号が混ざっててぱっと見ちょっとややこしいです。ややこしいです(^&^) ビット演算子でもなさそうだし…。dispatch_onceに関してくわしく調べていくことにします。
Mac Developer LibraryにあるGrand Central Dispatch (GCD) Referenceのdispatch_onceの項目を調べていきます。
先ず第一引数ですが、ブロックが完了したかどうかを示すポインタのようです。
dispatch_onceのコードを直接読むと実際に
void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
このように書かれていて、第一引数がポインターであることがわかります。
なのでC言語でポインターを扱うときの
int i; int *p; p = &i;
ポインターに変数のアドレスを代入するやり方を、引数で行ってただけなんですね。
なんかこう、いきなりC言語の書きかた出てきたりするあたり、頭では理解してても実際になるとだいぶ混乱してきます。
次に第二引数。こちらもどうやらC言語のブロックのようです。
Apple Developer Centerにあった並列プログラミングガイド(※注意:pdfで開きます)の40ページ目あたり
ブロックによるタスクの実装
ブロックは、コード中に、関数ポインタと同様の構文で記述します。ただし、ブロック名には、ア スタリスク(*)ではなくカレット(^)を前置します。
とあります。
これもC言語の書き方なのかな?
仮に宣言が
void(^hoge)void;
だと
hoge = ^{ //ブロック内の処理 };
こう書くようです。実際にdispatch_onceの第二引数で使われている形は、宣言も一緒に書くと
void(^)void; ^{ //ブロック内の処理 };
こんな感じですね。ActionScriptやJavaScriptの無名関数に近いと思います。
C言語から脈々と受け継がれる書き方を順序立てて理解していないと、今回のようにObj-Cの中に突如としてC言語が表れると混乱してしまって、シングルトンひとつ作るのにも時間がかかってしまったのが印象的でした。
わかるまでの時間が長いぶん、わかってしまえばあとは早いんですけどねえ…。
オレタチの戦いははじまったばかりだ!
とにかくXCodeとInterfaceBuilderがやっと使い方が腑に落ちて、やっとこやっとこObj-Cで実際に動くものを作りながら学習していけるようになったばかりですが、やっててちょっとしたことでも調べるために手が止まってばかりで、はじめてFlashを触ったときもこんなんだったな〜と思い出しながら楽しんでやっています。
この記事は自分が理解するために苦労した点ばかりなので、理解してしまえば書き留めておく必要のないものばかりかもしれません。実際「こんなしょうもない記事アップすんの??」と悩んだりもしましたが、せっかく書いたのでえいっと公開しておきたいと思います。