TitaniumMobile: 2011年8月アーカイブ

sxchu_1276335_threads.jpg

[Titanium] window.urlは推奨されないプログラミング手法らしい - もぎゃろぐの続きです。

つい先日まで使っていた物を突然駄目だと言われても納得が出来ないと思うので、どういう場面で困ったことになるのかの例を一つ。

ある程度大きなプロジェクトになると、アクセスクラス付きのデータクラスみたいなのを作りたくなることがあります。たとえばこんなクラス。

var Obj = function(){
  this.v = 1;
}
Obj.prototype.setVar = function(v){
        this.v = v;
}
Obj.prototype.getVar = function(v){
  return this.v;
}
var obj = new Obj();

obj.vを直接書き換える代わりに、セッターとゲッターを用意しておくことで、例えばセットした時にログを書くとか、セットする時に不正な値だったらはじくとか、いわゆるカプセル化のクラスです。
この状態で、objをあちこちの画面で共有したいとします。

var Obj = function(){
	this.v = 1;
}
Obj.prototype.setVar = function(v){
	this.v = v;
}
Obj.prototype.getVar = function(v){
	return this.v;
}
var obj = new Obj();
var win = Ti.UI.createWindow({});
win.obj = obj;
win.obj.setVar(2);
Ti.API.debug('app.js win.obj.getXXX:'+win.obj.getVar()); // 1が帰ってきてしまう

obj.setVar(2)で値を渡したはずなのですけど、newした時の値が帰ってきてしまいました。
この例では簡単にテストするためにファイル一個でやっていますが、winの定義を別ファイルに分けても再現します。

なんでこうなるの?

Ti.UI.createWindowで生成したオブジェクトは、JavaScriptのオブジェクトみたいに振る舞っていますけど、実際にはObjectiveCやJavaのオブジェクトにリンクされています(そうじゃないとネイティブUIの部品を画面に出すことが出来ないですからね)

ここからは推測なのですけど、そうやって生成されたWindowオブジェクトのプロパティに値を代入する(たとえば、win.obj = obj; のように)と、JavaScriptとObjectiveC/Java言語の壁を越えるために、いったんJSON文字列に変換されてしまいます。

結果としてメソッドは生き残ることが出来ないので、obj.setVar()がまともに動作しなくなります。

それだったらいっそ例外になってくれれば良いと思うのですけど、中途半端に動くあたり、もしかしたらJSON化じゃなくてもう少し別の実装になっているのかもしれない。その辺はちょっと自信なしです。ソース追ってみたんだけど糸口がわかんなかった(><)

オブジェクトはコンテキストを越えられない

ちょっと底の浅い説明になってしまいましたけど、ともあれ、obj.setVar()が動作しないことは事実です。この現象は、Windowオブジェクトに限らず、Ti.Appのプロパティとして割り当てても同じ現象が起きます。要するに、Ti.のプロパティにメソッドがついたオブジェクトを引き渡してはならない、ということです。

なぜwindow.urlを使ってはいけないのか。
window.urlスタイルのプログラムを使うと、上記のようなメソッド付きのオブジェクトを共有する手段がなくなってしまうからです。

単に数値や文字列を画面間で共有したいだけであれば、windowのプロパティに渡してもいいし、相互に変更をやりとりしたければ、Titanium.App.Propertiesを使うこともできます。
このやり方で実装してしまうと、各画面がロジック部分まで持つことになるので、UIとロジックを分離することが困難になってしまいます。
UIとロジックを分離するためには、メソッドの共有がどうしても必要なのです。

公式ビデオの三本目、Building Native Mobile Applications 03 - UI Fundamentalsでは、こんなふうに説明されていました。

「真っ平らな地面」が必要な場合のみ、マルチコンテキストを使う意味がある。KitchenSinkは、たくさんのデモを見せるための物で、互いにデータを共有する必要がなかったからマルチコンテキストが使われている。

あと、ロジック部分は全部インターネット上のサーバにあって、各画面はWebAPIを叩くだけ、というアプリも、マルチコンテキストで実装しやすそうな気がします。TitaniumMobileが得意とするカタログアプリみたいなのですね。



kimoi_girls.png
Titanium Mobileでは、ある程度大きなプロジェクトを作る場合、
var win = Ti.UI.createWindow({
	url:'hoge.js'
});
という具合にしてurl引数を使ってソースを分割するのが半ば常識だと思っていました。

ところが実は、これってあんまり推奨されないやり方なのだそうです。
Titaniumの公式トレーニングビデオの二本目、Building Native Mobile Applicationsの10:00あたり、 JavaScriptでファイル分割をする方法について解説しているくだり。

02CrossPlatformJavaScriptApplications_1030.png
一般論だけど、1ファイル1ウインドウのプログラミングモデルはオススメしないよ。
KitchenSinkがやっているけど、あれはデモ用だから。
1ファイル1ウインドウモデルを使うと、たくさんのコンテキストを管理するために多くの問題を抱えることになる。
もちろん、ウインドウのURLプロパティを使いたくなるような場面はあるだろうけど、 一般論としては、Ti.includeやcommon.jsスタイルのrequireを使って、共通のコンテキストでウインドウを開くことをオススメする。

(トレーニングビデオは英語ですけど、単語レベルで全部書き取る自信はなかったので、だいたいこういうことを言っていたよ、という私訳です。)

プロジェクトを根底から作り直す羽目になるくらい重要な話だと思うのですけど、他に書かれているのを見た覚えがないので、トレーニングビデオは一通り見ておく必要がありそうです。
あるいは、日本語公式セミナーに参加したら説明してくれるのかもしれない。

なお、「じゃあWindow.urlを使わずにどうやって作るの?」という疑問については、

を見るのが参考になります。後者は他のテクニックもてんこ盛りで規模が大きいので、まずは前者を見るのがいいかと。
僕もあとで別のブログ記事にまとめようと思っています。




このアーカイブについて

このページには、 2011年8月 以降に書かれたブログ記事のうち TitaniumMobile カテゴリに属しているものが含まれています。

前のアーカイブは TitaniumMobile: 2011年7月 です。

次のアーカイブは TitaniumMobile: 2011年9月 です。

最近のコンテンツは インデックスページ で見られます。過去に書かれたものは アーカイブのページ で見られます。

Powered by
Movable Type