Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード Twitter

2011-05-31

リファクタリング講座メモ

5/29にRainbowApps卒業生の方が主催された合宿に参加した際、そこでバスケさんが話されていた「公開リファクタリング講座」が非常にためになる内容だったので、そのときのメモを公開しておきます。


メモリまわりのデバッグ/リファクタリング

  • leaksが有名なのでそればかり使いがちだが、memory allocationやzombiesでも見てみること
    • live bytesが使用中のメモリ量
    • 1.5Mぐらいなら画像使用してるアプリなら多くない
    • 13Mになると、初代ipadなら落ちる可能性ある
    • live bytesでソートすると、今一番メモリくってるオブジェクトタイプがわかる
  • breakpointを右クリックでエディットできる
    • アクションの設定・・・10回通った後で止まる、とか。

f:id:shu223:20110606040105p:image:w520


  • バンドルからplistを読み込む動作は重い(一時オブジェクトが生成されるのでメモリ空間も汚れる)ので基本的には1度しかやらないこと

UIViewcontroller作成時の注意点

  • xibファイル名はクラス名と同じにする。同じ名前のxibファイルがあれば、initメソッドでnibからインスタンス化される(initWithNib〜とか書かなくてよい)
  • ヘッダには公開すべき内容だけ書く。外部に知らせる必要のないプロトコル名とかはクラスエクステンション機能を利用して.mに書く
    • プロトコル準拠もメンバ変数もここで宣言できる。
    • クラスエクステンションのフル機能を使うには、コンパイラにLLVMを選んでいる必要がある
@interface {class name}()
<UIAlertViewDelegate>
@end
  • プロパティ宣言しているものはメンバ変数の欄に書く必要がない
  • アウトレットも含め、すべてプロパティで管理する。処理のペナルティもないしメモリ管理が楽になる
  • viewDidUnloadでビューをself.xxx = nil;して、deallocで[self viewDidUnload];をコールする
    • この作法はappleからの回答もなくずっとグレーゾーンだったが最近海外の有名ライブラリはみんなこうしてる
    • viewDidUnloadを継承したら[super viewDidUnload];を必ずやること

  • Xcode4からGCCの代わりにLLVMが選べるようになった。
    • LLVMを利用するメリット
      • アップルは全てLLVMでコンパイルしてると宣言してる
      • クラスエクステンションのフル機能
      • コンパイラが賢い
      • デバッガも賢い

  • ViewController同士でお互いの参照を持つ構成は絶対だめ。解放されない。
  • デリゲートの代わりにKVO(key value observing)がおすすめ
    • NSNotificationを簡単にやるようなしくみ
    • キーとなる値(プロパティ)を監視する(キーパス:オブジェクトの値へのパス)
    • NSNotificationより粒度を細かくできる


2011-05-30

LLVM GCC と LLVM Compiler 2.0 とどっちがいいのか?

Xcode 4 からコンパイラのシステムデフォルトが GCC 4.2 から LLVM GCC 4.2 へ変更されましたが、その下にある LLVM Compiler 2.0 ってのが気になるので調べてみました。


LLVM GCC と LLVM Compiler 2.0 の違い

GCCと比較した場合、LLVMコンパイラでビルドするとアプリが高速化されるらしいのですが、LLVM GCC と LLVM Compiler 2.0でそういった違いはあるのでしょうか。

WWDCのスライドによると、


f:id:shu223:20110605160450p:image:w550


LLVM GCCはGCCでパースしLLVMで機械語を生成、LLVM Compilerはパース部分をClangが行いLLVMで機械語を生成、とのことで、LLVM GCC / LLVM Compiler どちらを用いても生成されるバイナリは同じ、つまり生成されるアプリの速度などの違いはなさそうです。


(2011/7/27追記)

申し訳ありません、y_naka様よりコメントいただきました内容によると、どうやら上記は誤りのようです。

どうやら生成されるバイナリは異なるもののようです.

適当なコード(手元にあったrsync3.0.8_0)で実験してみたのですが,llvm-gccとclangで生成された実行ファイルのサイズが異なりました.

llvm-gcc 436KB

clang 449KB



Clang Parser を使用するメリット

では LLVM Compiler で使用されている Clang Parser のアドバンテージって何?ってことを調べてみました。(参考情報は同WWDCのスライド)

  • コンパイルが高速・・・3倍速いそうです
  • エラー、ワーニングメッセージの改善・・・GCCの不親切で情報量が少ないメッセージを改善したとのこと(!)

(資料に挙がっていた例)

$ gcc test.m

test.m:2: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘*’ token


$ clang test.m

test.m:2:1: error: unknown type name 'NSstring'

NSstring *P = @"good stuff";


まとめ

個人的にはコンパイルエラー/ワーニングメッセージが改善されるってことでもうLLVM Compilerに切り替えてもいいんじゃないかなと思ってしまいました。

が、それでも今のところXcode 4のデフォルトはLLVM GCCなので、その理由が気になるところではあります。


下記のような資料もあるので、引き続き勉強していきます。



2011-05-29

GeoHexのレベルとサイズの対応

先日のgeohexエントリに引き続き、国産アルゴリズムGeoHexについてです。


下記コードで各レベルにおけるhexSizeをログにはいただけですが、コード書かずとも知りたい人もいると思うので、載せておきます。

for (int j=0; j<15; j++) {
    NSLog(@"level:%d, hexSize:%f", (j+1), [GeoHexV3 hexSizeForLevel:j]);
}

level:1, hexSize:6679169.446667

level:2, hexSize:2226389.815556

level:3, hexSize:742129.938519

level:4, hexSize:247376.646173

level:5, hexSize:82458.882058

level:6, hexSize:27486.294019

level:7, hexSize:9162.098006

level:8, hexSize:3054.032669

level:9, hexSize:1018.010890

level:10, hexSize:339.336963

level:11, hexSize:113.112321

level:12, hexSize:37.704107

level:13, hexSize:12.568036

level:14, hexSize:4.189345

level:15, hexSize:1.396448


hexSizeの単位はたぶんメートル。

MKMapViewを最大に拡大(MKCoordinateSpan:0.000351, 0.000429)した状態でhexを描画すると、レベル13(hexサイズ12メートル)ぐらいでギリギリ六角形が六角形として見える感じです。


f:id:shu223:20110605171211p:image:w240


レベルを最小の15にすると、黒い粒が見える程度・・・


f:id:shu223:20110605171231p:image:w240


逆に最大のレベル1は、日本列島をちょうど覆えるぐらいのサイズです。


f:id:shu223:20110605171248p:image:w240




ちなみに昨日の記事

これでこの土日に何か作ってみる予定です。

と書いたものはちゃんとそれっぽいものができたのですが、思いのほかつまらなかったのでお蔵入りにしました。。



2011-05-28

Objective-C版GeoHexを試してみました

GeoHexとは

独自の座標系を用いて世界中(GoogleMapsのカバー範囲)を隙間の無いHex(正六角形)で埋め尽くし、Hexによって世界中の全ての緯度経度の地点を表現するための仕様。Hexの大きさは25段階(レベル)で指定できる。

・見た目がcool!!

・隣接する6つのHex間の距離が全て均等。(正方形の場合、縦横と斜めで距離が違う。)

・隣接するHexのコードの算出が容易

といった特徴を持つ。

@sa2da さんが2009年11月に仕様を発表。

クリエイティブ・コモンズのライセンス形式に則り、指定のクレジットをサイト内に掲載することで自由に「改変」「再配布」「商用利用」が可能となっている。


こちらの記事を見つけて、GeoHexのObjective-C版を試してみました。


Githubに上がってるものをダウンロードしてビルドしようとすると、

error: GeoHex.h: No such file or directory

error: Zone.h: No such file or directory


というわけで、重要なコア部分のファイルがまるっと足りず、ビルドできませんでした・・・



で、別のGeoHex Objective-C版を発見したので、こちらも試してみました。

https://github.com/gillygize/GeoHex-ObjectiveC


こちら、Xcode4でプロジェクトを開くとターゲットがGeoHex-ObjectiveC-Textになっていて、ビルドしてもそれっぽいアプリにならないので焦るのですが、ターゲットをGeoHex-ObjectiveCに変更してビルドすると、こんな感じで地図上にHex(六角形)が描画されました。


f:id:shu223:20110528152550p:image:w240


ソースをみると、こんな感じでMKPolygonとうまく連携して地図上へHexを描画しているようです。

GeoHexV3 *geoHex3 = [[GeoHexV3 alloc] initFromCode:@"RU00667382"];
CLLocationCoordinate2D coordinates3[6];
NSArray *locations3 = [geoHex3 locations];
i = 0;

for (CLLocation *location3 in locations3) {
	coordinates3[i] = location3.coordinate;
	
	i++;
	
	if (i >= 6) {
		break;
	}
}

MKPolygon *polygon3 = [MKPolygon polygonWithCoordinates:coordinates3 count:6];
[mapView addOverlay:polygon3];

[geoHex3 release];

上記サンプルから、

  • HexコードからHexオブジェクトを生成する
  • HexオブジェクトからHexの6つの角の緯度経度を取得する

といった方法がわかります。


GeoHexV3.hを見て、他に何ができるか調べてみました。


・Hexの緯度経度や地図上でのX/Y座標を取得する

@property(readonly) CLLocationCoordinate2D coordinate;	/*!< The coordinate of the GeoHex. */
@property(readonly) MKMapPoint position;				/*!< The X, Y position of the GeoHex, using the GeoHex specific coordinate system */

・Hexのサイズを取得する

+(double)hexSizeForLevel: (int) aLevel;
-(double) hexSize;

・緯度経度からHexオブジェクトを生成する

-(id)initFromLocation:(CLLocationCoordinate2D) aLocation withLevel:(int)aLevel;

地図に表示される六角形って妙にわくわくしますね。

これでこの土日に何か作ってみる予定です。



2011-05-27

コードスニペットまとめサイト

UIViewのブロックアニメーションについて調べようと検索かけてみたら、偶然便利そうなサイトを発見しました。


Code Snippet Collection


試しにLanguagesのタブからObjective-cを選んでみると、

http://icodesnippet.com/language/objective-c/


f:id:shu223:20110531005504p:image:w550



ざくざく出てきます。かなり更新頻度は高いようです。


たとえば僕はanimateWithDuration:の繰り返しオプションを探してたのですが、下記のようなスニペットが出てきました。

[UIView animateWithDuration:1.5f // 1.5秒おきに
                      delay:0.0f // 0.0秒後から
                    options:UIViewAnimationOptionRepeat // 永遠に繰り返す
                                |UIViewAnimationOptionCurveEaseOut // 初めは速く終わりは遅くなるような変化
                                |UIViewAnimationOptionAllowUserInteraction // アニメーション中でもユーザによるViewの操作を可能にする
                 animations:^{
                                // このブロックの中にアニメーションの最終状態を記述する
                                self.alpha = 0.0; // alphaを0にする
                                self.bounds = CGRectMake(0, 0, 192, 192); // 波紋のサイズを192x192でframe全体に
                             }
                 completion:nil]; // アニメーションが終わっても何もしない
                  
                  
                  
//camera postでつかったやつ
[UIView animateWithDuration:1.5f // 1.5秒おきに
                      delay:0.5f // 0.0秒後から
                    options: // 永遠に繰り返す
 UIViewAnimationOptionCurveEaseOut // 初めは速く終わりは遅くなるような変化
 //|UIViewAnimationOptionAllowUserInteraction // アニメーション中でもユーザによるViewの操作を可能にする
                 animations:^{
                     // このブロックの中にアニメーションの最終状態を記述する
                     camBtnView.center = CGPointMake(0, 0); // alphaを0にする
                 }
                 completion:nil]; // アニメーションが終わっても何もしない

http://icodesnippet.com/search/animatewithduration/



サンプルソースが iPhone dev center で見つからない場合などにここで検索かけてみると良さそうです。



2011-05-21

UIKitのクラス名と、UI Automationでのクラス名の対応

UIAutomationをちょろっと使用してみたのですが、

「UIKitのあのクラスはUIAutomationでは何て名前?」

ってのをサッと知りたかったので、AppleのUIAutomationのリファレンスを見つつ、UIKitとUIAutomationのクラス対応表みたいなものを作ってみました。


ビューだったりコントローラだったり粒度はバラバラ、かつ未検証です。。


UIKitUI Automation
UIApplicationUIAApplication
UIWindowUIAWindow
UIActionSheetUIAActionSheet
UIActivityIndicatorUIAActivityIndicator
UIAlertViewUIAAlert
UIApplicationUIAApplication
UIButtonUIAButton
??UIAEditingMenu
UIViewUIAElement
??UIAKey
UITextInputTraitsUIAKeyboard
??UIALink
UINavigationBarUIANavigationBar
UIPageControlUIAPageIndicator
??UIAPicker
UIPickerViewUIAPickerWheel
UIPopoverControllerUIAPopover
UIProgressViewUIAProgressIndicator
UIScrollViewUIAScrollView
UISearchBarUIASearchBar
UITextFieldUIASecureTextField
UISegmentedControlUIASegmentedControl
UISliderUIASlider
UILabelUIAStaticText
??UIAStatusBar
UISwitchUIASwitch
UITabBarUIATabBar
UITableViewCellUIATableCell
??UIATableGroup
UITableViewUIATableView
??UIATarget
UITextFieldUIATextField
UIToolbarUIAToolbar
UIWebViewUIAWebView
UIWindowUIAWindow



2011-05-18

otoolでバイナリの内容をいろいろと調べる方法

new BSDライセンスについて調べていたら、こちらの有名な事件に関してのまとめ記事に行き当たり、


NatsuLion for iPhone クローン (略) から学んだこととかまとめ


その中でこんな記述がありました。

今回の場合は、まず、Twitterville が見た目上ソックリであっても、中身まで NatsuLion for iPhone のソースを使ったと示せないので、バイナリを otool してシンボルを全て比較するという手法を取りました。


へーそういうものがあるのか、とotoolの使い方などを調べてみました。


使い方

バイナリ(〜.app)のあるフォルダへ移動し、ターミナルから以下のように実行します。

otool -l {アプリ名}.app/{アプリ名}

バイナリのある場所は、Xcode4を使用しているのであれば、~/Library/Developer/Xcode/DerivedData から下を探せば見つかります。


また、AppStoreからダウンロードしたアプリを解析したい場合は、~/Music/iTunes/Mobile Applications/ 配下に {アプリ名}.ipa ファイルがあるので、こちらの拡張子を.zipに変更し、解凍すれば.appのバイナリが取り出せます。



otoolを使うとわかることその1:使用しているフレームワークの一覧

こんな感じで使用しているフレームワークの情報がわかります。

cmd LC_LOAD_DYLIB

cmdsize 88

name /System/Library/Frameworks/CoreGraphics.framework/CoreGraphics (offset 24)

time stamp 2 Thu Jan 1 09:00:02 1970

current version 600.0.0

compatibility version 64.0.0


otoolを使うとわかることその2:暗号化されたデータの情報

  • cryptoff:暗号化されたデータの開始位置(ファイル先頭からのオフセット)
  • cryptsize:暗号化されたデータの長さ
  • cryptid:暗号化されたデータの有無。0であればなし。

この情報を使って何かできてしまうようです・・・

AppStore Application Hack Guide


その他のオプション

オプションを変えれば上記以外にもいろいろな情報がとれそうです。

-a Display the archive header, if the file is an archive.

-S Display the contents of the `__.SYMDEF' file, if the file is an archive.

-f Display the universal headers.

-h Display the Mach header.

-l Display the load commands.

-L Display the names and version numbers of the shared libraries that the object file uses. As

well as the shared library ID if the file is a shared library.

-D Display just install name of a shared library.

-s segname sectname

Display the contents of the section (segname,sectname). If the -v flag is specified, the sec-

tion is displayed as its type, unless the type is zero (the section header flags). Also the

sections (__OBJC,__protocol), (__OBJC,__string_object) and (__OBJC,__runtime_setup) are dis-

played symbolically if the -v flag is specified.

-t Display the contents of the (__TEXT,__text) section. With the -v flag, this disassembles the

text. And with -V, it also symbolically disassembles the operands.

-d Display the contents of the (__DATA,__data) section.

-o Display the contents of the __OBJC segment used by the Objective-C run-time system.

-r Display the relocation entries.

-c Display the argument strings (argv and envp) from a core file.

-I Display the indirect symbol table.

-T Display the table of contents for a dynamically linked shared library.

-R Display the reference table of a dynamically linked shared library.

-M Display the module table of a dynamically linked shared library.

-H Display the two-level namespace hints table.



参考ページ

Appleのotoolリファレンス



2011-05-16

BMPファイルのカラーパレットを書き換える

ゲームのキャラ画像など、一つの画像に対して配色パターンがたくさんある場合、画像ファイルは1つだけ持っておいて、カラーパレットを動的に書き換えるという方式をとるようです。

これだと容量を節約できるし、全ピクセルに対して色変換する場合と違ってパレットの書き換えだけなので4バイト×256の処理で済みます。


BMPファイルフォーマットの詳しい説明はこちら。

http://www.kk.iij4u.or.jp/~kondo/bmp/


これの、

  • RGBQUAD(Blue, Green, Red, Reserved の 4 byte)
  • 256色ビットマップ(BitCount == 8)

の場合についての変換コードを書いたので載せておきます。

(※注:下記コードはBitCount の値が 1, 4の場合や、RGBTRIPLE には対応しておりません)


+ (NSData *)convertImageData:(NSData *)data WithPalette:(NSArray *)palette {
    NSMutableData *dataOut = [NSMutableData dataWithCapacity:0];

    // ファイルヘッダ 	14
    // 情報ヘッダサイズ	4
    // 幅				4
    // 高さ				4
    // プレーン数			2
    // ピクセル毎のビット数	2
    // 圧縮タイプ			4
    // イメージデータサイズ	4
    // 水平解像度			4
    // 垂直解像度			4
    // カラーインデックス数	4
    // 重要インデックス数	4
    
    // ピクセル毎のビット数
    unsigned char pic_per_bit[2];
    [data getBytes:pic_per_bit range:NSMakeRange(14+4+4+4+2, 2)];
    
    // パレットが存在するための条件にあてはまらなければnilを返す
    // ※QUADタイプに限定
    if (*pic_per_bit != 8) {
        NSLog(@"warning : no palette");
        return nil;
    }

    // ヘッダ部分をコピー
    int offset = 14+40;    
    [dataOut appendData:[data subdataWithRange:NSMakeRange(0, offset)]];

    
    // パレット書き換え (青, 緑, 赤, 予約)
    unsigned char b;
    unsigned char g;
    unsigned char r;
    unsigned char a;
    
    for (int i=0; i<[palette count]; i++) {
        NSDictionary *paletteDic = [palette objectAtIndex:i];
        
        NSNumber *paletteB = [NSNumber numberWithInt:[[paletteDic objectForKey:@"ColorB"] intValue]];
        NSNumber *paletteG = [NSNumber numberWithInt:[[paletteDic objectForKey:@"ColorG"] intValue]];
        NSNumber *paletteR = [NSNumber numberWithInt:[[paletteDic objectForKey:@"ColorR"] intValue]];
        NSNumber *paletteA = [NSNumber numberWithInt:[[paletteDic objectForKey:@"ColorA"] intValue]];
        
        b = [paletteB unsignedCharValue];
        g = [paletteG unsignedCharValue];
        r = [paletteR unsignedCharValue];
        a = [paletteA unsignedCharValue];
                
        
        [dataOut appendBytes:&b length:1];
        [dataOut appendBytes:&g length:1];
        [dataOut appendBytes:&r length:1];
        [dataOut appendBytes:&a length:1];
        
        offset += 4;
    }
    
    [dataOut appendData:[data subdataWithRange:NSMakeRange(offset, [data length] - offset)]];


    return dataOut;
}

画像をバイナリデータとして処理するため、UIImageではなくCGImageなんとかでもなく、NSDataとして扱います。


呼び出し側では、下記のように画像ファイルからNSDataオブジェクトを作成し、上記メソッドに渡しています。

NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:nil];
NSData *dataIn = [NSData dataWithContentsOfFile:path];
NSData *dataOut = [PaletteConversion convertImageData:dataIn WithPalette:palette];


ちなみにパレットデータはこんな感じのplist形式のファイルを読み込んでます。

<array>
	<dict>
		<key>ColorA</key>
		<string>0</string>
		<key>ColorB</key>
		<string>144</string>
		<key>ColorG</key>
		<string>144</string>
		<key>ColorR</key>
		<string>144</string>
	</dict>
	<dict>
		<key>ColorA</key>
		<string>255</string>
		<key>ColorB</key>
		<string>176</string>
		<key>ColorG</key>
		<string>224</string>
		<key>ColorR</key>
		<string>248</string>
	</dict>
	...(これが256個)
</array>

ColorAとかのキーが冗長な感じですが、諸事情あってこうなっております。よしなに修正してご使用ください。



2011-05-14

インストールしているアプリ一覧を共有するサービスappsfireのしくみ

ここで開発者の方が自らヒントを出してくれています。

http://forums.macrumors.com/showthread.php?t=1121778

I'm a Product Manager at Appsfire. We're happy you've found the app and hope you're enjoying.

Everything we do is within Apple's guidelines and plays by the rules that all other apps play by. Think about using FB Connect within an app on on iPhone - you're directed to Facebook's app for authentication and then redirected back to the app you connected from. Nothing magical.

「ちゃんとガイドラインに沿った、他のアプリでもやってる方法だよ」と、FB Connectを例に挙げています。


ってことは間違いなく、以前書いたデバイスにインストールされているアプリ一覧を取得する

アプリのカスタムURLスキーム一覧を持っておいて、そのカスタムURLスキームが使用可能であればインストールされていると判断する

の方法をとっていると思われます。


実際にアプリを使ってみると、


f:id:shu223:20110606034232p:image:w240


このようにインストールされているアプリが全てリストに挙がってくるわけではないことからも、カスタムURLスキームを見ていることが推測されます。




2011-05-11

csvファイルからplistを生成するPHPスクリプト

エクセルとかGoogle docsで編集されたデータをplistファイルとしてアプリに持たせたい、といった場合用の、csvファイルからplistファイルへの変換スクリプトです。


こちらに置いてあります。

https://github.com/shu223/csv2plist


たとえばこういうcsvファイルを読み込ませると、

(sample.csv)

id,name,age,birth
0,tsutsumi,32,5/26
1,sato,27,1/11
2,tanaka,21,11/3

こんな感じでplistファイルを出力します。

(sample.plist)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
	<dict>
		<key>id</key>
		<string>0</string>
		<key>name</key>
		<string>tsutsumi</string>
		<key>age</key>
		<string>32</string>
		<key>birth</key>
		<string>5/26</string>
	</dict>
	<dict>
		<key>id</key>
		<string>1</string>
		<key>name</key>
		<string>sato</string>
		<key>age</key>
		<string>27</string>
		<key>birth</key>
		<string>1/11</string>
	</dict>
	<dict>
		<key>id</key>
		<string>2</string>
		<key>name</key>
		<string>tanaka</string>
		<key>age</key>
		<string>21</string>
		<key>birth</key>
		<string>11/3</string>
	</dict>
</array>
</plist>

使い方

ターミナルから次のように実行します。

$ php csv2plist.php -t hoge.csv

CSVファイルにタイトル行がない場合は、-tのオプションなしで実行してください。

$ php csv2plist.php hoge.csv

補足

plistファイルは、こんな感じで initWithContentsOfFile: メソッドに渡してやるだけで NSArray や NSDictionary のオブジェクトとして取り扱えるようになります。

NSString *path = [[NSBundle bundleForClass:[self class]] pathForResource:@"hoge" ofType:@"plist"];
NSArray *arr = [[[NSArray alloc] initWithContentsOfFile:path] autorelease];

  • あまり変更のないデータはplistで
  • 変更があるけど項目が増えていかないデータ(設定値とか)はNSUserDefaultで
  • 変更があって数に増減のあるデータはDBで

っていう使い分けが定石かと思います。



2011-05-09

構造体を NSDictionary や NSArray に格納する方法

C言語の構造体をNSDictionary や NSArray、NSUserDefaultsなどに格納する方法、つまりObjective-Cのオブジェクト化する方法についてです。

CGRectとかCGPointを格納するときと同様、NSValueを用います。


例として下記のようなRGBAの色情報を持つ構造体を定義します。

// 構造体の宣言
typedef struct {
	unsigned char r, g, b, a;
} RGBA;

格納する場合は、value:withObjCType: メソッドを使用します。

// 構造体をNSDictinaryに格納する
RGBA rgba = {255,255,255,255};
NSValue *value = [NSValue value:&rgba withObjCType:@encode(RGBA)];
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:value, @"rgba", nil];

ポイントは、

  • valueには構造体のポインタを渡すことと、
  • withObjectTypeには@encode(RGBA)と、構造体の型情報をエンコードして渡すこと

です。


@encode()って見慣れないですが、「型エンコード」と呼ばれるもので、こちらのアップルのドキュメントによると、

ランタイムシステムを支援するために、コンパイラは各メソッドの戻り型と引数型を文字列にエンコードし、その文字列とメソッドセレクタを関連付けます。また、コンパイラが使用するエンコード方式は他のコンテキストでも便利に使用することができるため、@encode()コンパイラディレクティブを通じて一般に利用できるようになっています。型指定を渡すと、@encode()はその型をエンコードした文字列を返します。

というものです。


取り出す際は、格納先の構造体のポインタをNSValueのgetValueメソッドに渡してやります。

// 構造体をNSDictionaryから取り出す
RGBA rgba;
[(NSValue *)[dic objectForKey:@"rgba"] getValue:&rgba];

参考ページ



2011-05-07

CCDirector のソース解析

CCDirectorのソースを解析してたときのメモがあったので、載せておきます。

(UIKitとcocos2dの併用時にCCSceneの遷移でハマっていたので、原因の目星をつけられるようソースを読んでました)


CCDirectorの正体

CCDirector *director = [CCDirector sharedDirector];

とすると、


+ (CCDirector *)sharedDirector
{
	if (!_sharedDirector) {

		//
		// Default Director is TimerDirector
		// 
		if( [ [CCDirector class] isEqual:[self class]] )
			_sharedDirector = [[CC_DIRECTOR_DEFAULT alloc] init];
		else
			_sharedDirector = [[self alloc] init];
	}
		
	return _sharedDirector;
}

とシングルトンが生成されるのですが、ここでalloc/initされてる CC_DIRECTOR_DEFAULT は


#define CC_DIRECTOR_DEFAULT CCDirectorTimer

と定義されてるので、実は CCDirectorTimer (CCDirectorのサブクラスであるCCDirectorIOSのサブクラス)のインスタンスが生成されています。


CCDirectorTimer

アニメーションのフレーム処理はNSTimerで実装されてるようです。

- (void)startAnimation
{
	// (略)
	
	animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval_ target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];
}


-(void) mainLoop
{
	[self drawScene];
}

CCDirectorIOS

- (void) drawScene
{    
	// (略)
	
	/* draw the scene */
	[runningScene_ visit];
	
	/* draw the notification node */
	[notificationNode_ visit];

	// (略)
}

CCNode

-(void) visit
{
	// (略)
	[self draw];
	// (略)	
}

CCNodeのdrawメソッドは空。

CCNodeのサブクラスで実装。たとえばCCSpriteでは


-(void) draw
{
	NSAssert(!usesBatchNode_, @"If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called");

	// Default GL states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Needed states: GL_TEXTURE_2D, GL_VERTEX_ARRAY, GL_COLOR_ARRAY, GL_TEXTURE_COORD_ARRAY
	// Unneeded states: -

	BOOL newBlend = blendFunc_.src != CC_BLEND_SRC || blendFunc_.dst != CC_BLEND_DST;
	if( newBlend )
		glBlendFunc( blendFunc_.src, blendFunc_.dst );

#define kQuadSize sizeof(quad_.bl)
	glBindTexture(GL_TEXTURE_2D, [texture_ name]);
	
	long offset = (long)&quad_;
	
	// vertex
	NSInteger diff = offsetof( ccV3F_C4B_T2F, vertices);
	glVertexPointer(3, GL_FLOAT, kQuadSize, (void*) (offset + diff) );
	
	// color
	diff = offsetof( ccV3F_C4B_T2F, colors);
	glColorPointer(4, GL_UNSIGNED_BYTE, kQuadSize, (void*)(offset + diff));
	
	// tex coords
	diff = offsetof( ccV3F_C4B_T2F, texCoords);
	glTexCoordPointer(2, GL_FLOAT, kQuadSize, (void*)(offset + diff));
	
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	
	if( newBlend )
		glBlendFunc(CC_BLEND_SRC, CC_BLEND_DST);
	
#if CC_SPRITE_DEBUG_DRAW
	CGSize s = [self contentSize];
	CGPoint vertices[4]={
		ccp(0,0),ccp(s.width,0),
		ccp(s.width,s.height),ccp(0,s.height),
	};
	ccDrawPoly(vertices, 4, YES);
#endif // CC_TEXTURENODE_DEBUG_DRAW
	
}

ここでOpenGL ESベースの描画処理を行っていることがわかります。



2011-05-06

MKAnnotationViewのコールアウトをカスタマイズする

地図をタップしたときに出るフキダシをカスタマイズする方法です。


f:id:shu223:20110606065727p:image:w483

参考ページより)


参考ページではかなり詳細に書かれているのですが、コールアウトのカスタマイズに関わる部分の要点だけ抜粋すると、

  • MKAnnotationViewのサブクラスをつくり(サンプルではCalloutMapAnnotationView)、
  • そのsetAnnotation:で位置とフレームを決め、
  • drawRect:メソッド内で描画

ということを行っているようです。


(その他の参考ページ)

http://stackoverflow.com/questions/1350050/custom-mkannotationview-callout



2011-05-05

OpenAL、AVAsset を使用した無料アプリをリリースしました

左右で別々の音源を聴くことが可能な、学習加速アプリです。


f:id:shu223:20110606075355p:image

i聖徳太子

※無料です



やりたいことはシンプルなのに、

  • iPodライブラリから取得した曲を
  • 左右にパンニングして再生

という必須要件を満たそうとするとそのまま MPMediaItem をMPMusicPlayerController で再生、とはいかず、

ということをしています。


ファイルに書き出してしまえばパンニングできるプレイヤークラスはOpneAL以外にもある(AVAudioPlayerとか)のですが、せっかくなら2ch以上にも対応しようってことで3D音響なOpenALを採用しました。


コードの詳細な解説等は機会があれば別途エントリーを書こうと思います。



2011-05-04

バッテリー消費量の計測方法など、iOSのデバッグに関するドキュメント

NSZombieEnabledをYESにするとか、そういう開発ノウハウの情報ソースはどこにあるんだろうと不思議だったのですが、iOS Dev Centerのドキュメントを漁っていて発見しました。


iOS Debugging Magic


たとえばNSZombieEnabledなどの話は、Table 10 Foundation environment variables にあります。


以下、使えそうだと思った情報。


UIView の階層構造を書き出してくれるメソッド

UIViewはdescription メソッド以外に recursiveDescription というメソッドを備えていて、これを使うとビューの階層構造を書き出してくれるようです。

<UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
Current language: auto; currently objective-c
(gdb) po [[self view] recursiveDescription]
<UIView: 0x6a107c0; frame = (0 20; 320 460); autoresize = W+H; layer = […]
   | <UIRoundedRectButton: 0x6a103e0; frame = (124 196; 72 37); opaque = NO; […]
   |    | <UIButtonLabel: 0x6a117b0; frame = (19 8; 34 21); text = 'Test'; […]

つい先週僕が作ったものはまさに車輪の再発明だったようです。。


パケット追跡

パケット追跡方法について書かれたQAへのリンク。

The most critical tool for debugging network code is the packet trace. Technical Q&A QA1176, ’Getting a Packet Trace’ discusses how to get a packet trace on Mac OS X.


バッテリー消費量の計測方法

"Energy Diagnostics"というInstrumentsの機能でバッテリー使用量に関する計測が可能なようです。

The Energy Diagnostics instrument is a great way to understand how your application affects battery life. See Instruments User Guide for details.


そんなのあったっけ?と思ってInstruments立ち上げてみたらありました・・・


f:id:shu223:20110504180759p:image:w533


今度試してみます。


アプリのフレームレートを計測する

Instrumentsの"Core Animation"という機能でフレームレートを計測できるようです。

The Core Animation instrument lets you measure your application's frame rate and see various types of drawing. See Instruments User Guide for details.


そんなのあったっけ?と思ってInstruments立ち上(略)


f:id:shu223:20110504180826p:image:w534


InstrumentsはLeaks以外の機能も何ができるのかぐらいは調べてみようと思います。


その他

  • Push Notifications
  • Assembly-Level Objective-C Debugging
    • アセンブリレベルでのデバッグでの注意点。こういう方法もあるんだ、と頭の片隅に置いとく程度に。。

日本語版

最終更新日は2004-12-02でmac版ですが日本語版もあります。

http://developer.apple.com/jp/technotes/tn2124.html

情報ソース


おまけ



2011-05-03

AdMobの入金タイミング

AdMobの収益が振り込まれるタイミングがよくわからなかったので調べてみました。


http://helpcenter.admob.com/ja/content/お支払い方法

サイト運営者様へのお支払いスケジュール


月々の収益の支払い処理は、収益があった月の翌々月に開始されます。 処理には通常約 1 週間かかりますが、支払い処理の開始時には通知メールをお送りします。 この日をもってお客様の AdMob アカウントから支払い金額が差し引かれ、アカウントの概要ページに支払い日が記録されます。


たとえば、1 月に 500 ドルの収益があった場合、3 月の第 1 週に支払い処理が開始され、翌週に送金されます。

ただし、月末時点で収益が 20 ドル未満の場合は翌月に繰り越させていただきます。 たとえば 1 月に 10 ドル、2 月に 50 ドルの収益があった場合は、4 月上旬から中旬に 60 ドルが支払われます。

支払いと送金の履歴は、アカウント タブのアカウントの概要からご確認いただけます。

というわけで、ほっといても支払い処理はちゃんと開始されます。

僕の個人アカウントでは最初に収益があったのが3/19で、支払い処理開始連絡が5/3に来ました。



2011-05-02

Instruments の Leaks の見方(Live Bytes や Living の意味)

f:id:shu223:20110503004250p:image:w570


このLive BytesやらLivingやら、タイトルから大体推測できるものの、ちゃんとした定義をしりたかったのですが、ヘルプ内を探しても見当たらず、iOS開発関係の本を見てもどの値が何とまでは書いてくれていませんでした。たまたまググったら出てきたので、下記にまとめておきます。


Live Bytes

The Live Bytes column indicates how many of this type of object have been allocated and still are around in memory.

該当するタイプのオブジェクトの現在のメモリ使用量


# Living

# Living column tells you how many objects of this type are still around in memory.

該当するタイプのオブジェクトがメモリに残っている数


Overall Bytes と # Overall

The Overall Bytes and # Overall columns show the total size in memory and number of all allocated objects of that type, whether or not they are still around in memory at this time.

メモリに残ってないものも含めた、該当するタイプのオブジェクトの合計メモリ使用量と数


# Transitory

# Transitory is simply the difference between # Living and # Overall, showing how many objects of that type were created and destroyed during the time period you are analyzing.

Living と Overallの差分。


参考ページ



2009 | 08 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2014 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2015 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2016 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 11 | 12 |
2017 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2018 | 02 |