- C# (.cs) はUTF-16
- JS (.js) はUTF-8
- Boo (.boo) は不明
- ネイティブのiOSはUTF-8
最終的にJSファイルに全ての日本語リテラルをまとめて回避しましたが、そもそも日本語リテラルは使わず国際化文言ファイルみたいなものを別途用意しておいたほうが筋が良いと思います。iOSならLocalizable.stringsがありますしね。
Jan 8 11:01:25 akisute-no-iPhone sociald[6444]SLComposeViewControllerが裏で使用しているsocialdのSLDFacebookPostUploadクラスがエラーを吐いています。一見しただけだとOAuthExceptionとか書いてあって認証系のエラーかと勘違いしてしまいそうですが、実はこれSLComposeViewControllerのaddURL:メソッドに「空の」NSURLインスタンスを渡した時に発生するエラーです。以下のコードで簡単に実験することができます。: 2013-01-08 11:01:25.665|sociald|0x1fd65e70: Request status was 400 Jan 8 11:01:25 akisute-no-iPhone sociald[6444] : 2013-01-08 11:01:25.668|sociald|0x1fd65e70: SLDFacebookPostUpload: The response indicates an error Parameters: {type = immutable dict, count = 1, entries => 2 : {contents = "error"} = {type = immutable dict, count = 3, entries => 0 : {contents = "message"} = {contents = "(#100) The parameter link is required"} 1 : {contents = "type"} = {contents = "OAuthException"} 2 : {contents = "code"} = {value = +100, type = kCFNumberSInt64Type} } } . HTTP status 0
ps aux | grep unityとか叩けばなんとなくわかります。
12/1 akisutesama 12/3 tokibito 12/4 torufurukawa 12/8 shimizukawa
このrspファイルにはC#コンパイラのコンパイラオプションとして渡すオプションが記述できます。ですので http://linux.die.net/man/1/mcs などに列挙されているコンパイラオプションであればひと通りなんでも使用可能です。今回はプリプロセッサ定義をやりたいので、以下のように書けばOKです。/Assets/smcs.rsp (C#用) /Assets/gmcs.rsp (C#エディタスクリプト用) /Assets/us.rsp (UnityScript、俗にJSと呼ばれている物用) /Assets/boo.rsp (Boo用)
-define:DEBUG複数の定義が書きたい場合は、
-define:DEBUG -define:USE_DEV_SERVERのようにスペースで空けて複数記載するか、
-define:DEBUG -define:USE_DEV_SERVERのように改行して複数記載するか、
-define:DEBUG;USE_DEV_SERVERのようにセミコロンで列挙して書くことができます。注意点として、C/C++/Objective-Cのdefineマクロと違って
-define:DEBUG=1のように値を定義することはできないみたいです。あくまでシンボルを作るだけみたいですね。
@akisutesama -define:HOGE -define:FUGA みたいに書けば複数定義出来ますよ。行を分けてもOK。ただしレスポンスファイル(rsp)を修正したらスクリプトは再コンパイルされる必要があるのでスクリプトをすべて再インポートしてください
— Hiroki Omae (@pigeon6) November 9, 2012
@akisutesama 念のために書いておくと.rspファイルはヘンな黒魔法ではなく応答ファイル(response file)といって、要するにコンパイラに渡すコマンドラインオプションをファイルに書いておけるという方式です。なので中身はただのC#コンパイラオプションでございます
— Hiroki Omae (@pigeon6) November 9, 2012
はい、mcsのmanページでよいと思います。オプションに関する情報ではないですがこちらも未読なら読んでおくと良いと思います mono-project.com/CSharp_CompilerRT @akisutesama: @pigeon6 ですよね、コンパイラオプションですよね?ってことは…
— Hiroki Omae (@pigeon6) November 9, 2012
@akisutesama 応答ファイルの利用はUnity的には公式機能ではないので、あまりやりすぎないようにした方がよいです。Unityはビルド時にコードの最適化を行ったり、特にiOSではAOTをやったりと結構事後処理を頑張っているので、そこでおかしくなる可能性はあります。
— Hiroki Omae (@pigeon6) November 9, 2012
AssetDatabase.Refresh(); reimportSomethingToForceRecompile(); private void reimportSomethingToForceRecompile() { var dataPathDir = new DirectoryInfo( Application.dataPath ); var dataPathUri = new System.Uri( Application.dataPath ); foreach( var file in dataPathDir.GetFiles( "GlobalDefinesWizard.cs", SearchOption.AllDirectories ) ) { var relativeUri = dataPathUri.MakeRelativeUri( new System.Uri( file.FullName ) ); var relativePath = System.Uri.UnescapeDataString( relativeUri.ToString() ); AssetDatabase.ImportAsset( relativePath, ImportAssetOptions.ForceUpdate ); } }これでC#で書かれたコードに対しては無事にrspファイルへの変更が反映されるようになります。
00000000-0000-0000-0000-000000000000
を返してしまうらしいのです!iOS 6.0.1では修正されているらしいです。00000000-0000-0000-0000-000000000000
でないか文字列比較する方法がよさそうです。sudo gem install xcodeprojあとは上記のPostprocessBuildPlayerをUnityプロジェクトの /Assets/Editor 以下に配置して、コード中のフレームワーク名を指定している箇所をご自身のお好きなように変更してやればオッケーです。
@pigeon6 こないだのUnityで回転で吹っ飛ぶ理由がわかりました! Game Centerみたいっす。 developer.coronalabs.com/forum/2012/09/…
— akisute (@akisutesama) September 24, 2012
@akisutesama ありがとうございます。supportedInterfaceOrientationsForWindow/shouldAutorotate のまわりの対応ということなら対応中です。3.5.6で直すつもりみたい。
— Hiroki Omae (@pigeon6) September 25, 2012
UIViewController shouldAutorotateToInterfaceOrientation:が完全に廃止になっており、基本的には全く呼び出されないようになってしまっています。その代わりにより体型だった画面回転の仕組みが導入されています。iOS 6からの画面回転は、「アプリが対応する画面方向」と「各View Controllerが対応する画面方向」の2つによって画面の回転する方向が決定されるという仕組みになっています。
以下優先順位順に、 1. UIApplicationDelegate application:supportedInterfaceOrientationsForWindow: が返す向き。iOS 6以降のみ。 2. UIApplication supportedInterfaceOrientationsForWindow: が返す向き。iOS 6以降のみ。 3. Info.plist で指定されている UIInterfaceOrientation の向き。各View Controllerが対応する画面方向は以下のように決定されます。
各ViewControllerが supportedInterfaceOrientations を実装しているなら、それが返す向き。 していないならば、 iPhoneだと UIInterfaceOrientationMaskAllButUpsideDown iPadだと UIInterfaceOrientationMaskAllこの2つの積集合をとって、両方が一致した向きに画面回転が行われる仕組みになっています。
[[NSBundle mainBundle] pathForResource:@"MyMusic" forType:@"mp3"];みたいなコードを書いたことがあると思います。このBundleの仕組みを使うと、
/Assets/Plugins/iOS
以下にBundleを配置しても正しくBundleが認識されませんし、iOSのアプリにインストールされません。/Assets/Plugins/iOS/
以下ではなくて、/Assets/StreamingAssets/
以下にBundleを配置するようにしましょう。実は/Assets/StreamingAssets/
以下のパスには隠し要素があって、このパス以下のファイル・ディレクトリは全てアプリケーション自身の/Data/Raw/
ディレクトリに配置されるようになっています。NSBundle *bundle = [NSBundle bundleWithPath:[[NSBundle mainBundle] pathForResource:@"MyBundle" ofType:@"bundle" inDirectory:@"Data/Raw"]];これでUnityプラグインを多言語化したり画像リソースを使ったりするのが楽になると思います。
/Assets/Plugins/iOS/
以下においたBundleも扱えるようにしてくれると楽なんですが・・・Unity 3.5の地点ではどうもダメっぽいです。
lldb
コマンドで LLDB の対話環境を起動した後、process attach --name MyApp --waitforでMyApp.appという名前のプロセスが立ち上がるのを監視します。
breakpoint -f MyTestModel.m -l 123とかやって適当にブレークポイントを設置した後、
thread continueで続きを実行開始します。
__weak MyViewController *__weakSelf = self; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){ __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding]; }];このようなコードを安全に実行するためにはselfを一度__weakな変数に代入して、それをBlocksにキャプチャさせるようにしないと、以下のような理由で安全ではなくなってしまいます。
__has_feature(objc_arc_weak)
と__has_feature(objc_arc)
マクロです。こいつらを使うと簡単に現在のビルド環境・ターゲット環境がARCを導入しているか、weakは使用可能か、を判断できます。たとえば私は以下の様なマクロを組んで、// ARC & memory management // Use these prefixes to be compatible with ARC on iOS 5/ ARC on iOS 4.X / non-ARC // #if __has_feature(objc_arc_weak) // iOS 5 or above #define __my_block_weak __weak #define __my_block_weak_unsafe __weak #elif __has_feature(objc_arc) // iOS 4.X #define __my_block_weak __strong #define __my_block_weak_unsafe __unsafe_unretained #else // iOS 3.X or non-ARC projects #define __my_block_weak __strong #define __my_block_weak_unsafe __block #endifこんな感じのコードにしてます。
__my_block_weak MyViewController *__weakSelf = self; [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse* response, NSData* data, NSError* error){ __weakSelf.label.text = [[NSString alloc] initWithData:data encoding:NSUTF8Encoding]; }];こうするとどの環境でも(比較的)安全にblocks内部でselfを触ることができるという寸法です。
__has_feature(objc_arc_weak)
の判定条件です。個人的にこいつはiOS SDK 5.0以上を使っていたらターゲット環境がiOS 4とかiOS 3とかでも無条件で1を返してしまって使えないんじゃないのかと危惧していたのですが、なんとIPHONEOS_DEPLOYMENT_TARGET
の値を見てきちんと値が変化する仕組みになっています!なので例えばSDKは最新だけれどiOS 3もサポートしたいみたいなプロジェクトでは__has_feature(objc_arc)
の値が自動的に0になって良い感じに分岐してくれるというわけです。安心して使いまくりましょう!
map
- 条件式を渡して、もとの集合の各要素に条件式を通した結果を新たな集合として返す。reduce
- 条件式を渡して、要素を前から順番に計算して畳み込み、集合から一つの要素にする。any
- 一つでも要素が条件式を満たすならtrue, すべての要素が満たさないならfalseall
- すべての要素が条件式を満たすならtrue, 一つでも満たさないならfalsestring と System.String は型として使われる限り単なる別名なので同じものです。
foo.System.String みたいなものが用意されて foo 名前空間内から使うような場合は異なりますが,特殊例過ぎるのでこれは考えないものとします。
コンパイラは,ソース上の型として string と書かれているものは,System.String とかかれているものとして処理します。
# 正確には "System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" という型 (.NET 4 の場合)
これは,SDK 付属の逆アセンブラでコードを見ると,string を使っても System.String を使っても同じコードになることから確認できます。
同じように,int は System.Int32 の別名ですし,bool は System.Boolean の別名です。
使い分けは,特別決まりはありません。
唯一,メソッド名等に使う場合には言語固有の名称を避ける (CLR の名前を使う),というのはあります。
MSDN: 一般的な名前付け規則
http://msdn.microsoft.com/ja-jp/library/ms229045.aspx
具体的には,
・ToInt
よりも,
・ToInt32
の方が好ましい,ということです。
# 上側の名前付け規則だと,Visual Basic においては ToInteger であるべき,となる。
私は,static メソッドの呼び出しに使う場合は CLR の名前を使い,それ以外では言語固有の名称を使っています。
書籍によっては,常に CLR の名前を使うべき,という物もあったりします。
# が, for (Int32 i = 0; ……みたいなコードはほとんど見ません。
自分なりの物でいいので,統一したルールを用意しておくのがよいでしょう。
# 常に言語固有の名称でもよいと思います。
Wacom iPad/IPad2/iPhone4対応 描画、ポインティングに最適なタッチペン Bamboo Stylus CS-100/K0 ワコム 2011-05-27 by G-Tools |
iPad/iPhone用スタイラスペン Su-Pen P101M-AS 7knowledge by G-Tools |
寺西化学工業 マジックラッションペン No.300 黒 寺西化学工業 by G-Tools |
xxd -i Sample.pngとすると、
unsigned char Sample_png[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x07, 0xfd, 0x00, 0x00, 0x04, 0x73, //中略 unsigned int Sample_png_len = 589903;このような感じでバイナリデータがC言語のヘッダファイルになって出力されます。あとはこれをNSDataにして、UIImageを生成することができます。NSDataを作る際には余計なデータコピーが発生しないdataWithBytesNoCopy:length:freeWhenDone:を使うことをお勧めします。
NSData *data_Sample_png = [NSData dataWithBytesNoCopy:Sample_png length:Sample_png_len freeWhenDone:NO]; UIImage *image = [UIImage imageWithData:data_Sample_png];
sudo easy_install gcovrインストールが完了したら、あとは
gcovr --xml --output=cobertura-report.xml build/とかやれば一発でbuildディレクトリ配下のすべてのカバレッジを計測結果を集計してXML形式で書きだしてくれます。
gcovr --xml --exclude=".*/.*Test(s)?\.[(c)(cpp)(m)(mm)]" --output="cobertura-report.xml" build/などとすればOKです。
Cmd+U
一発でテストケースを実行することができない。Xcode経由だとCmd+R
でアプリケーションとして起動して、その後起動したアプリのテスト実行ボタンを押す手間が必要になるため、開発のテンポが乱されてしまう。特にTDDを採用している場合には深刻。cd Project-iOS makeでビルドしてできたGHUnitIOS.frameworkを使うようにしてください。
- (void)testOCMock { // Creating a new stub object from class { id mockedObject = [OCMockObject mockForClass:[NSString class]]; STAssertThrows([mockedObject length], @"Mocked object raises exception because the fake method is not ready yet."); STAssertThrows([mockedObject isKindOfClass:[NSString class]] , @"Mocked object can't even call isKindOfClass: because it's not ready."); STAssertFalse([mockedObject class] == [NSString class], @"Mocked object is not a kind of the target class."); [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length]; STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value."); [[[mockedObject stub] andReturn:@"AllYourBaseAreBelongToUs"] lowercaseString]; STAssertEqualObjects([mockedObject lowercaseString], @"AllYourBaseAreBelongToUs", @"Returns mocked value."); STAssertEquals([mockedObject length], (NSUInteger)100, @"Previously mocked methods are still valid."); } // Method Stubbing for existing object { NSString *stubTargetObject = @"I am a stub target."; STAssertEquals([stubTargetObject length], (NSUInteger)19, @"The original implementation of the length method."); id mockedObject = [OCMockObject partialMockForObject:stubTargetObject]; STAssertTrue([mockedObject isKindOfClass:[NSString class]] , @"Mocked object is a kind of the target class."); STAssertEquals([mockedObject length], (NSUInteger)19, @"Mocked object returns the original value."); [[[mockedObject stub] andCall:@selector(mockedLength) onObject:self] length]; STAssertEquals([mockedObject length], (NSUInteger)100, @"Mocked object returns the fake value."); STAssertEquals([stubTargetObject length], (NSUInteger)100, @"Stubbed target object also returns the fake value."); } } - (NSUInteger)mockedLength { return 100; }
xcodebuild -project MyApp.xcodeproj -configuration Release -target MyApp clean build xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator5.0 clean build xcodebuild -project MyApp.xcodeproj -configuration Debug -target MyAppTests -sdk iphonesimulator clean build上の例ではデフォルト設定のiOS SDKを用いてビルドターゲットMyAppがReleaseビルドされます。次の例ではiPhone Simulator 5.0 SDKを用いてビルドターゲットMyAppTestsがDebugビルドされます。一番下の例では、手元にある最新のiPhone Simulator SDKが使用されるようです。また -target の代わりに -scheme を使うことも出来ます。ビルドに使うことができるSDKの一覧を得るためには、
xcodebuild -showsdksを使えばよいです。
Cmd+U
を押して実行できる単体テストが、xcodebuild経由では実行できません。これについては後ほどまた触れます。(gdb) print 0xfee65c0 $1 = 267281856 (gdb) print (void *)0xfee65c0 $2 = (void *) 0xfee65c0こんなとき、この void* が指し示している先の型がわかりきっている場合は、その型でキャストしてやって:
(gdb) print (struct imap_session_state_data *)0xfee65c0 $4 = (struct imap_session_state_data *) 0xfee65c0 (gdb) print $4 $5 = (struct imap_session_state_data *) 0xfee65c0参照先にアクセスすればきちんと中身が見えます:
(gdb) print * $4 $6 = { imap_session = 0xfee6560, imap_mailbox = 0xfee6230 "INBOX", imap_flags_store = 0xfee6490, imap_ssl_callback = 0, imap_ssl_cb_data = 0x0 } (gdb) print $6->imap_session $7 = (mailimap *) 0xfee6560 (gdb) print * $7 $8 = { imap_response = 0xfee6090 "FETCH completed", imap_stream = 0xfeecc90, imap_progr_rate = 0, imap_progr_fun = 0, imap_stream_buffer = 0xfee6a00, imap_response_buffer = 0xfee6a20, imap_state = 3, imap_tag = 4, imap_connection_info = 0xfee64d0, imap_selection_info = 0xfee6030, imap_response_info = 0xfee60e0, imap_sasl = { sasl_conn = 0x0, sasl_server_fqdn = 0x0, sasl_login = 0x0, sasl_auth_name = 0x0, sasl_password = 0x0, sasl_realm = 0x0, sasl_secret = 0x0 }, imap_idle_timestamp = 0, imap_idle_maxdelay = 1740, imap_body_progress_fun = 0, imap_items_progress_fun = 0, imap_progress_context = 0x0 }これでデバッグがはかどりました。
platform :ios dependency 'ASIHTTPRequest' ,'~> 1.8' dependency 'JSONKit' ,'~> 1.4' dependency 'BlocksKit' dependency 'MagicalRecord'ARCあり・なしのライブラリを混ぜても全く問題ありません。素晴らしい!!
# まずは対象のリポジトリをcloneしてくる # ここでは相手のリポジトリを直接使ってますが、github上でforkして、そっちを使うようにしてもいいです。forkしたほうが自分で自由にコードに改変を加えたりtagを打ったりできますのでよいかも。 git clone https://github.com/MugunthKumar/MKNetworkKit.git # 移動 cd MKNetworkKit # 対象のバージョンにHEADを移動します git reset --hard v0.8a # podspecファイルのひな形を出力します pod spec create MKNetworkKitこれでMKNetworkKit.podspecファイルが出力されますので、今度はこのファイルを書き換えます。先ほどcloneしてきたライブラリのソースコードとプロジェクト設定を見ながら、必要なソースコード、必要なリソース、不要なファイル、ライブラリやフレームワークなどのビルド設定を考えて、適切な設定を用意しなければなりません。
Pod::Spec.new do |s| s.name = 'MKNetworkKit' s.version = '0.8a' s.license = 'MIT' s.summary = 'Full ARC based Networking Kit for iOS 4+ devices' s.homepage = 'https://github.com/MugunthKumar/MKNetworkKit' s.author = { 'MugunthKumar' => 'mknetworkkit@mk.sg' } s.source = { :git => 'https://github.com/MugunthKumar/MKNetworkKit.git', :tag => 'v0.8a' } s.source_files = 'MKNetworkKit/*.{h,m}', 'MKNetworkKit/Categories/*.{h,m}' s.clean_paths = 'MKNetworkKitDemo', '*.xcodeproj', 'sample.JPG' s.frameworks = 'CFNetwork' s.requires_arc = true s.dependency 'Reachability', '~> 2.0' end大事なのはsource, source_files, frameworks, requires_arc, dependencyぐらいです。あとは自分しか使わないならでたらめでかまいません。
pod spec lint MKNetowrkKit.podspec何か問題があれば何かエラーが出ます。修正しましょう。何も無ければ何も出ません。
/podspecのs.name/podspecのs.version/先ほど作ったpodspecファイルたとえば今回の例では:
/MKNetworkKit/0.8a/MKNetworkKit.podspecという名前で配置する必要があります。私が試した際は、間違ってると正しくpodspecファイルを認識してくれませんでした。ファイルを配置したらこのリポジトリをgithubにpushします。
pod repo add myrepo リポジトリのURLこれでmyrepoという名前でリポジトリが登録されます。
~/.cocoapods/
以下を覗いてみると、確かに myrepo という名前のリポジトリが追加されているはずです。pod install MyProject.xcodeproj
を実行。