頭と尻尾はくれてやる!

iOSアプリなどの開発日記です


さすが傑作!「坂の上の雲」

「坂の上の雲」を読了したよ。
最終巻を読もうとしたら、かなり前に予約してたSteve Jobs1が回って来て、人気本だからとっとと読んでさっさと返せよ、延長はなしだぞ、ごらあ!って図書館の人に言われたので先にそっちを読んだんだよ。最終巻はいよいよバルチック艦隊との日本海決戦じゃぁ!とワクワクしてたもんだから、ちょびっとジョブズがうざかったなあ(こちらも面白かったけどね)。

それにしてもこれは、さすが司馬遼太郎の2大代表作の1つと言われるだけのことはあるね。もう一つは「竜馬がゆく」らしいんだけど、個人的には「坂~」の方が面白かったかな。あまりこの時代の話に詳しくなかったので新鮮だったというのもあったけどさ。
どちらも傑作で子供が大きくなったらどちらも絶対に読ませるよ。
俺たちのご先祖様は苦難の時代にこんなに頑張ったんだ、というのはやっぱり知っておくべきだよね。


最終巻に出てきた言葉をメモ。どこかで聞いたことあったけど、元はここだったんだね。

信濃丸より「敵艦見ゆ」=「敵の艦隊、二〇三地点に見ゆ。時に午前四時四十五分」
「敵艦見ゆとの警報に接し、連合艦隊は直ちに出動、之を撃滅せんとす。本日天気晴朗なれども波高し」
Z旗「皇国の興廃、此の一戦に在り。各員一層奮励努力せよ。」
スポンサーサイト






NSCodingでNSData化するとサイズがかなり大きくなる?

GKSessionクラスを使ってNSData型のデータをヘッダー付きで送信することはできた、ってのが前のエントリー(GKSessionでヘッダーとNSDataを送信する方法)なんだけどさ、これをいじってる時にふとデータのサイズが妙に大きくなってるんじゃない?って気がしたんだ。

ちょっとくらい大きくなるくらいだったらいいんだけど、GKSession使って送信するんだからやっぱりなるべく小さい方がよさそうじゃない?
AppleのドキュメントにだってGKSessionでデータ送信するデータのサイズはなるべく1000バイト以下にせえよって記述もあるしね。
最高のパフォーマンスを引き出 すには、データオブジェクトのサイズを小さく(長さ1000バイト以下に)することをお勧めしま す。1000バイトより大きいメッセージは、小さい固まりに分割して送信先で組み立てなおさなけれ ばならないことがあります。それにより遅延やオーバヘッドが増える可能性があります。
*Game Kit プログラミングガイドより


そこでどのくらいのサイズになりそうなのか簡単なテストをしてみたんだよね。
次のソースはstring1とstring2って文字列をNSDataに変換してそのサイズを見るって内容なんだ。二つの文字列はともに5文字ずつだよ。
-(void) testTransfer
{
    NSString *string1 = @"Hello";
    NSString *string2 = @"World";
    
    //case1
    NSString *allString = [NSString stringWithFormat:@"%@,%@",string1,string2];
    NSData *data = [allString dataUsingEncoding: NSUTF8StringEncoding];
    NSLog(@"[data length] = %d[bytes]",[data length]);//11[bytes]
    
    
    //case2
    Transfer *transfer = [[Transfer alloc] init];
    transfer.array = [NSArray arrayWithObjects:string1,string2, nil];
    NSData *transferData = [NSKeyedArchiver archivedDataWithRootObject:transfer];
    NSLog(@"[transferData length] = %d[bytes]",[transferData length]);//313[bytes]

        
    //case3
    Transfer2 *transfer2 = [[Transfer2 alloc] init];
    transfer2.string1 = string1;
    transfer2.string2 = string2;
    NSData *transfer2Data = [NSKeyedArchiver archivedDataWithRootObject:transfer2];
    NSLog(@"[transfer2Data length] = %d[bytes]",[transfer2Data length]);//260[bytes]
    
}
case1はNSStringのdataUsingEncoding:メソッドを使ってNSData型に変換してるよね。
この時二つの文字列を区切るのにコンマを使っているからトータルで5+5+1で11バイトになったんだな。まあこれは納得だよね。

case2はお待ちかねのNSCoding使ってNSData型にする場合で、二つの文字列を配列(NSArray)に放り込んで、それをNSData型にしてるよ。
エンコードのためにTransferってクラスを作ったんだけど、もちろんこのクラスはヘッダーにNSCodingプロトコル使うぜって宣言してるからね。
Transferクラスでは次のようにしてエンコードしてるんだよね。
//in 'Transfer' class
- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:_array forKey:@"array"];
}
ここで_arrayはTransferクラスのプロパティだよ。
そうそう、NSCodingプロトコル使うって宣言するとデコードの部分も必須だけどここでは省略してるよ。
結果はというと、313バイトになったんだ。
これちょっとすごくなくなくなーい?ってくらいの驚きだね、だって元は10バイトのデータなんだよ?
あ、これってもしかしてNSArray使ったからかな?と思って今度はもう少し直球勝負してみたんだ。
それがcase3なんだけど、次のように配列に入れるんじゃなくて文字列としてエンコードしてみたんだよね。
//in 'Transfer2' class
- (void)encodeWithCoder:(NSCoder *)encoder
{
    [encoder encodeObject:_string1 forKey:@"string1"];
    [encoder encodeObject:_string2 forKey:@"string2"];
}
このcase3の結果は260バイト。
ちょっとだけ減ったけど元が10バイトのデータかと思うとやっぱりでかいよね。
実際に使う場面を考えたら簡単に1000バイトを超えそうだもんな。

これは困ったな、、、しょうがない、隠し味にオリーブオイルを使うしかないね!


GKSessionでヘッダーとNSDataを送信する方法

いくつかのiOSデバイス間でデータのやりとりをする時に使うGKSessionクラスってあるじゃない。これを使いたくていろいろとテストしてたんだ。
GKTankってすごく参考になるサンプルがアップルにあって、それを使うと実際にデータのやりとりをしているのを確認できたんだよ。
ただ、GKTankでは次のような構造体を送信してるんだ。
typedef struct {
	CGPoint		tankPreviousPosition;
	CGPoint		tankPosition;
	CGPoint		tankMissilePosition;
	CGPoint		tankDestination;
	
	float		tankRotation;
	float		tankDirection;
	float		tankMissileDirection;
	int			tankMissile;
} tankInfo;
他にも次のような整数型のデータを送信してるんだよね。
int gameUniqueID;
GKTankのコードを見ると、これらのデータにヘッダーを付けて送っているんだ。
ヘッダーにはアプリ開始時からカウントされる番号や送信するデータの種類を記述してるんだ。そりゃそうだよね、送信するデータがいったいどういう構成なのかわからないと受信した方でも困っちゃうよね。

で、いざ自分も同じようにやってみようとして困ったのが、NSData型のデータを送信したいんだけど、やり方がよくわからなかったんだ。
GKTankだと上記のように構造体とか数値とかそういうのばかりなので、勝手が違ったんだよね。

送信したいのはNSData型といったけど、別に配列のNSArrayでも、文字列のNSStringでも日時を表すNSDateでもなんでもいいんだ、だってNSCoding使えばそれらはどれもNSData型にすることができるもんね。

何時間もGoogle先生に聞いた結果ようやく動くコードになりましたってのが次のコード。
送信部分が二つに分かれているのはGKTankのコードに倣っているというかそのままというか。
いろんなタイプのデータを送るわけだけど、それらはいろんなメソッドで記述すればいいんだけど、どれも最終的には送信部分2をコールするってイメージなんだ。
あと、GKTankとちがって送信部分2でヘッダーはデータのタイプを示す1つしかないよ(オリジナルは2つあるよね)。
-(void)sendButtonTappedHandler
{
    //送信部分1

    NSString *message = @"Hello";
    NSData *messageData = [message dataUsingEncoding: NSUTF8StringEncoding];   
    int length = [messageData length];//5
    
    [self sendNetworkPacket:session_ packetID:1 withData:(void*)[messageData bytes] ofLength:length reliable:YES];
    
}

- (void)sendNetworkPacket:(GKSession *)session packetID:(int)packetID withData:(void *)data ofLength:(int)length reliable:(BOOL)howtosend 
{
    //送信部分2
    static unsigned char networkPacket[kMaxPacketSize];
    const unsigned int packetHeaderSize = sizeof(int); 
    
    if(length < (kMaxPacketSize - packetHeaderSize)) {
		int *pIntData = (int *)&networkPacket[0];
		// header info
		pIntData[0] = 1;//packetID;
        
		// copy data in after the header
		memcpy( &networkPacket[packetHeaderSize], data, length ); 
		
		NSData *packet = [NSData dataWithBytes:networkPacket length:(length+packetHeaderSize)];
        [session_ sendData:packet toPeers:[NSArray arrayWithObject:peerId_] withDataMode:GKSendDataReliable error:nil];
        
	}
}
最初データの数え方がよくわからなくてさホント困っちゃったよ。
もともとは'sizeof'とかになってて、整数型とかならそれでいいんだろうけど、NSDataでそれやるとポインタ分(番地を表す数バイト分)だけという間抜けなことになっちゃうよね。
ここは'malloc_size'だなんてGoogle先生が教えてくれたりしたので混乱もしたけど、どいうやら上記のようにNSDataのlengthメソッドで取ればいいみたいよ。

送信したら今度は受信する方だよね。
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peer inSession:(GKSession *)session context:(void *)context
{ 
    
    unsigned char *incomingPacket = (unsigned char *)[data bytes];
    int *pIntData = (int *)&incomingPacket[0];
    
    int packetID = pIntData[0];
   	
    switch( packetID ) {
        case 1:
        {
            NSData *messageData = [NSData dataWithBytes:&incomingPacket[4] length:[data length]-4];
            NSString *message = [[NSString alloc] initWithData:messageData encoding:NSUTF8StringEncoding];
            NSLog(@"message is %@",message);
            
        }
            break;
        
        default:
			break;
    }

}
NSDataのデータを作るというか戻すのに、最初は次のようなコードを書いてたんだけどうまくいかなかったんだ。

NSData *messageData = (NSData *)&incomingPacket[4];
元々のソースコードを眺めているとこれでいいのかなと思うんだけど、結果的には長さをちゃんと指定しないとうまくいかなかったよ。
そんなわけで、dataWithBytes:length: メソッドを使ってる。length:の部分で4バイト分引いているのはもちろんヘッダー分ね。

えらく苦労しちゃったんだけど、とりあえずデータを送信することが無事できたよ。今日は赤飯だね!


Xcode 4.3がやってきた

来たねえ、Xcode 4.3が。
てっきりもうすぐあるって言われてる次期iPadの発表会で、iOS 5.1が正式リリースしてさ、ここで同時にXcode 4.3正式版がリリースされると思ってたんだ。
そのXcode 4.3をみると、なるほどシミュレータを次期iPad選択すると発表通りRetinaになってるねえ、って筋書きかと思っていたんだけど大はずれだったね。
なんせ開発者向けにリリースされてる最新のはXcode 4.3 Developer Preview 3でこれってiOS 5.1 beta 3用だったから、てっきりXcode 4.3はiOS 5.1になってからのものだと思ってたんだよ。

で、この新しいXcode 4.3はダウンロードしていいの?とか悩んだんだけど、迷ったらとりあえず新しいモノ入れておけって神のお告げに従って、ダウンロードしようとしたんだけど、Mac App StoreとiOS Dev Centerのどちらがいいんだろ?今使っているXcode 4.3 Developer Preview 3はどうしたらいいんだろ?
とか迷ったんだけど、結局Mac App StoreでDeveloper Preview 3はほったらかしでダウンロード開始しちゃったよ、いいのかね?

ダウンロードにだいたい15分くらいかかったんだけど、無事に成功。

Xcode 4.3

とにかくこれでbeta版じゃなくて正式版になったからアプリの修正申請なんかもできるな。


Xcode 4でLocalizable.stringsを追加する方法

自分のアプリが世界中で販売されるなんてホント楽しいよね。iOSアプリ作成の醍醐味だよね。
「赤ちゃんの成長グラフ」は16カ国語に対応してるんだ。だから世界中のユーザーから「てめえのアプリのドイツ語が変だから何とかせえや!」みたいな応援メールが来るんだよ。やりがいあるよね!
そんなわけで、今作ってる時計アプリも多言語化、ローカライズしようかと思ってるんだ。

でも、いざLocalizable.stringsファイルってどうやって作るんだったっけなあってさんざん迷っちゃったよ。
前の時はどうやったんだろうな?そもそも、前の時はまだXcode4じゃなくて、強引にXcode4に持って来たりしてもうわけがわからなくて何が何だかって状況だったんだよ。とりあえず動いているけどさ。

今回はXcode4で初めてLocalizable.stringsファイルを作ろうとしたんだけど、、、ホントどうするんだろうね?
とりあえず、できたことを書いとくけど話半分程度に聞いてね。

New Fileで新規ファイル追加

↑まず、Xcode 4上で、New File …ってのでファイルを追加しようとしたんだ。

Emptyを選択

↑するとどんなファイルだよって聞かれるじゃない。それらしいのがないので、Emptyってのを選んだんだ。

Localizable.stringsと手入力

↑ファイル名を手入力で"Localizable.strings"って入力してやったよ。


これで、ナビゲーションエリア(Xcode 4画面左側)にLocalizable.stringsファイルが追加されたでしょ?


じゃあ次にこのファイルを他言語化するんだけど、やり方覚えてる?
ファイル(ここではLocalizable.stringsだね)を選択しといてから、ユーティリティーエリアにある"Localization"ってのを探そう。
ユーティリティーエリアってのは普段みんなどうしてるのか知らないけど、俺は普段は隠しているんだ。あまり使わないもんね。
Xcode 4の画面右側に隠れてるからあらかじめそいつを表示させておいてね。

そのユーティリティーエリアにある"Localization"ってところで"+"ボタンで追加したい言語を追加すればできたよ。
今回は英語と日本語を選んだんだ、その結果がこれ

多言語化

これ、最初は何にもなかったけど、EnglishとJapaneseってのが見えるでしょ?

そこで、再度ナビゲーションエリアにあるLocalizable.stringsのところを見てみるとLocalizable.stringsが多言語化されて複数ファイルになってるでしょ?

言語ごとのLocalizable.stringsファイルが存在

↑ほらね。
後は各言語ごとにごにょごにょしちゃえばコード内でNSLocalizedStringで一撃だねっ!

だけど、ホントこんなのでいいのかな。まあちゃんと動いてるけどさ。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.