2012年8月31日星期五

[iOS]NSURLConnectionのThread問題

NSURLConnectionを利用する場合、Threadの問題を注意する必要がある。
要求によって、http requestに非同期と同期の二つ方式を選択する場合がよくある。

  • 同期
基本はUIThreadに実行される。
UITheadに使う時、connectionWithRequest或はstartを使えば、問題なく送信ができる。
非UIThreadの場合、sendSynchronousRequest或はperformOnMainThread:selector(実はsendSynchronousRequestと同じだと思う)を使う、効果としてはUIThreadに実行していると同じ。

  • 非同期
非同期なので、必ず非UIThreadで実行している。
ここは注意するところ。
一般的に、NSURLConnectionを使用する場合、protocolをimplementする必要がある。なので、そのprotocolに従って定義したmethodを実行するThreadが終了したら、requestも正しく処理できない。異常現象としてはrequestが送信できない、エラーも出ない。これもUIThreadがよく使われる原因だ、UIThreadが終了しないから。
解決策の方法がたくさんあるが、原理は一つだ:何とかして、methodを実行するThreadを死なせないように。例えば、flagを立てってwhile loopを掛ける、或はCFRunLoopRun()とCFRunLoopStopを使うと、処理が完成する。

[Titanium]NSLogによるdebug messageが出力しない問題


昨日自分が作ったTitanium Moduleをテストする時、一部のログが出ない問題が大変だった。一部新規したclassに書いたNSLogがちゃんと出てきたが、linkしたlibraryとして使うsource fileに書いたNSLogが無反応だった。debug flagが漏れたと思ったが、checkしたらちゃんと付いている。
仕方ないTitaniumのsourceを分析する道を選ぶしかない、それて原因を見つけた。簡単に言うとmacroの責だ。
現象と原因は以下のように:

  1. TiProxy或はTiUIViewを承継したclassが問題なく、全部のNSLogを出力した。しかし、NSLogに出力したmessageが<Warning>として出力するはずだったが、<Notice>として出力した。原因はTitaniumの方NSLogをmacroで全部書き直した。Noticeはprintfを使った結果なんだ。
  2. macroを書いたのはTiBase.hだ。このheader fileをimportすれば、ログが全部出る。一つ一つのfileにimportを追加するのは面倒なので、prefix.pchにflagを付けって一気にimportする。
    #ifdef DEBUG
        import "TiBase.h" 
    #endif
    
これですべてのログが出力されるはず。

2012年8月28日星期二

[Titanium]iOSのModule作成

最近ずっとTitaniumの開発を触っている。Titaniumを利用してアプリを作る資料が多かったが、module作成についてはかなり資料が少ない気がする。忘れないために、ここに書き残っておく。

Titaniumをdownloadし、installした後、新規module projectから新しいmoduleを作成し始める。wizardに従って、projectを作成した後、x-code projectが自動に生成する。しかし、作成されたx-code projectが直ぐにコンパイルができない、titanium.xcconfigファイルにtitaniumのpath環境設定の方を確認することがある。moduleにSecurity.frameworkを使う場合、module.xcconfigに

OTHER_LDFLAGS =$(inherited) -framework Security
を追加する。 整った後、moduleのObjective-C codeを作成します。


  1. [TiModule]
  2. defaultとして、TiModuleを承継したクラスが既に自動作成されている。簡単なmethodであれば、このクラスにmember methodを追加して、javascript側から下記の様に使われます。
    var Module = require('xxxModuleId');
    Module.xxxMethod(args); 
    
  3. [TiProxy]
  4. moduleをオブジェクト指向に作りたい場合、TiProxyを承継してクラスを作る作成されたクラスがjavascript側にオブジェクトとして作成られる。referenceに従って、propertyが、member mothodが作った後こうやって利用する。
    var Module = require('xxxModuleId');
    var proxy = Module.createXXXProxy();
    proxy.xxxMethod(args);
    
  5. [TiUIView & TiViewProxy]
  6. UIViewを作成したい場合、TiUIViewとTiViewProxyを承継して、二つのクラスを組合せて作成必要がある。UIViewを実装するのがTiUIViewを承継する方、呼ぶinterfaceとしてはTiViewProxyを承継する方を利用する。
    TiUIViewを承継したクラスにUIViewを定義し、frameSizeChangedに実装する。公開するinterface methodがTiViewProxyに追記する。
  • ExXxxView.h
  • @interface ExXxxView : TiUIView {   
        UiView * mainView;
    }
    @end
    
  • ExXxxView.m
  • @implementation ExXxxView
    -(void)dealloc
    {
        RELEASE_TO_NIL(mainView);
        [super dealloc];
    }
    
    -(void)frameSizeChanged:(CGRect)frame bounds:(CGRect)bounds {
        if (mainView == nil )mainView = new UiView;
        // do something
    }
    @end
    
    これで、javascriptに利用
    var fooView = Module.createView({
        "color":"red",
        "width":20,
        "height":20
    });
    containerWindow.add(fooView);
    
    複数Viewがある場合、上記のdefault method createView()が使えないと思うので、customizeで自分のcreate methodを作る。
  • ExXxxViewProxy.h
  • @interface ExXxxViewProxy : TiViewProxy {
    }
    
    // customize create method
    -(id)createXxxView:(id)args;
    @end
    
  • ExXxxViewProxy.m
  • @implementation ExXxxViewProxy
    -(id) createXxxView:(id)args {
        // return the mainView defined in ExXxxView.h
        return [self view];
    }
    @end
    
    又javascriptに利用
    var fooView = Module.createXxxView({
        "color":"red",
        "width":20,
        "height":20
    });
    containerWindow.add(fooView);
    
    create methodに渡したparametersをproxyにget/setを定義必要がありません、objective-cに直接取ることができる。
    NSInteger intValue = [TiUtils intValue:[self.proxy valueForKey:@"key"]];
    

2012年8月21日星期二

[iOS]duplicate interface declaration

今日 Objective-C のduplicate interface declaration for class XXXの問題に引かれた。
この問題になる原因は三つにまとめた:
  1. #importの代わりに#includeを使用したせいで、重複参照
  2. #同じinterfaceを二回以上定義した
  3. 今度の原因、参照するlibraryのlib.aと共に参照したheader filesの中の一つがlib.aに書き込まれたheader fileとversionが違いそうで、もう一回compileして、導入し解決しました。
追加:
  1. BプロジェクトのソースF1をAプロジェクトにreferenceで参照している場合、F1でimportしたF2がBのF2だ、もしAに同じ名前でF2が存在すれば、エラーになる。BのF1をAにcopyで参照すれば、解決する。

2012年8月20日星期一

[Win]win-shellのescape文字

dos batや、win-shellのエスケープ文字は:
^
例:
echo a=1 ^| b=2
「|」はパイプコマンドですが、ここでは文字としてprintします。

2012年8月19日星期日

[PG]readonlyのclassをinterfaceにするべきじゃない

TitaniumのiOS版 App-In-Purchaseを対応しようとしている、SKProduct, SKPaymentTransactionなとiOS APIが提供しているclassに困った。これらのclassに保存された属性情報全部readonlyになっている。
既存の自作APIソースがそれをそのままinterfaceとして使った。今外から、APIを呼びたいだが、SKProductを直接渡せることができなくて、setすることもできないため、新しいAPIを追加しなければならない。
だからAPIを作る時、パラメータとし、自分がコントロールできないClassをなるべく使用しないことに。

2012年8月18日星期六

[ListView]onItemClick event無効の問題

複数種類のViewを含むcustomizeのitemを持つ ListView がよく使われますが、不注意するとitemのclick eventを監視するOnItemClickListenerが無反応になります。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp" >

    <ImageView
        android:id="@+id/btn_star"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:contentDescription="@string/description_star"
        android:src="@android:drawable/btn_star" />

    <TextView
        android:id="@+id/text_name"
        android:inputType="textMultiLine"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_toLeftOf="@id/btn_share" />

</RelativeLayout>


上記のXML中に問題になったのはTextViewのandroid:inputType="textMultiLine" 。これでClickする時に、FocusをTextViewに奪われる、Item全体のClickEventが無効になってしまう。Touch Eventが全部旨くdispatchされましたが、どうやらlistenerが呼ばれなかった。