統計情報(30日間)


最新情報をツイート


人気の投稿

60 fps を実現する超高速イメージキャッシュ

このエントリーをはてなブックマークに追加

Path謹製のイメージキャッシュライブラリ。イメージ表示にかかる様々なオーバーヘッドを減らすことで 60fpsを実現している。またメモリのフットプリントも従来方式の半分と省メモリ。


先に種を明かすと非圧縮のイメージデータをキャッシュすることでかかるオーバヘッドを減らしている。その特性上サムネイル画像など小さいサイズのイメージが向いている。またオリジナルイメージの保存管理は行わない。
githubのreadmeにはライブラリで使われているテクニックが丁寧に解説されていて非常に参考になる。それ自身が高速化のヒントになるのでライブラリを使わなくても一読をおすすめ。


高速化テクニックのポイントは次の通り。

1. 単一ファイルで複数イメージを管理 (Image Table)
複数イメージを単一ファイルで管理し、さらにファイルは最初の一回オープンした後、mmapでメモリへマップしたままクローズしないで使い続ける。これによって複数ファイル毎のオープン・クローズのオーバヘッドを減らしている。また mmapにより必要な時にディスク→メモリへの読み込みが行われる(遅延処理)。

2. 非圧縮イメージを保存
イメージを表示する時の一番のオーバーヘッドはデコード処理(非圧縮処理)。これを避ける為にオリジナルイメージ(JPEGなど)からデコードした後の非圧縮形式のデータを保存しておき表示する時にはこれをそのまま使う。この特性上通常よりも使用するディスク容量が多くなる。例えば 100x100pixelのサムネイルを 250個キャッシュすると 4bytes/pixel x 100 x 100 x 4 = 10MB消費。掲載されているデモプログラムの比較では通常 568KBに対して2.2MBのディスク使用だった。

3. バイトアライメント(byte-alignment)調整
データが非圧縮であってもイメージを描画するCoreAnimationが扱うバイトアライメントに合致しないとその変換のためにデータコピーが発生してこれがオーバヘッドとなる。このオーバヘッドを無くす為にキャッシュ作成時にあらかじめバイトアライメントをCoreAnimation向けに調整しておく。

デコードやメモリーコピーといったオーバーヘッドを無くして、ディスクから読み出されたデータがそのまま描画される流れを作ることで高速化を実現している。


その他、非同期のAPIを提供、50x50,100x100といったように複数のサムネイルセットを扱えるようになっていたりと実用的。ただAPIの使い方は若干煩雑なので慣れが必要か(でも難しくはない)。


ドキュメント最後には従来方式との比較デモがビデオ付きで掲載されている。それぞれの各種値の比較表

非圧縮なのでディスク容量は増えてしまうがフレームレートは約2倍出ていて劇的に速くなっている。
フットプリント(RPRVT = amount of resident memory consumed by our heap allocations)が半分になっているのも特徴的。デコードやメモリコピーを避けたことによってその分消費メモリも激減している。


- - -
いいなーこれ。
久々に面白いテクニックが見られてワクワクした。

脱獄とクラック検出ライブラリ

このエントリーをはてなブックマークに追加

2つのクラスメソッドで簡単検出。

[Crackify isJailbroken];
[Crackify isCracked];



ソースを見るといろいろな方法を組み合わせているのがわかる。

・クラック検出は、コード署名者のIDをチェックしたり、Info.plistなどの各種リソースの変更日時をチェックしている。

・一方、Jailbreakは Cydia.app などのアプリやライブラリファイルの存在をチェックしている(わざわざ @"App", @"lic",@"ati", @"ons/", @"Cyd", @"ia.", @"app"なんてリテラルを分解してる)。通常書き込みができない /private/へファイルを書き込むなんてテストまでしてるのは面白い。

tumblr製 モダンな非同期2段キャッシュライブラリ

このエントリーをはてなブックマークに追加

非同期と2段キャッシュのアイディアは秀逸。



特徴
・key/valueインターフェイス
・ディスクキャッシュ、メモリキャッシュの2つから構成される (*)
・GCDを使った非同期な動作(保存と取り出し)
・メモリキャッシュはメモリ不足やバックグラウンド移行時に自身をクリアする
・APIは同期・非同期の両方が用意されている
・指定日時以前のキャッシュを消すことができる(trimToDate:)
・iOS, OSX 両方で使える

*...ソースをざっと読んだ限りでは、両方へ書き込み、取り出しの時にはメモリキャッシュ→ディスクキャッシュ順で取得。2段構え!


使い方はこんな感じ。保存は非同期。時間がかかってもブロックされない。
UIImage *image = [[UIImage alloc] initWithData:data scale:[[UIScreen mainScreen] scale]];
[[TMCache sharedCache] setObject:image forKey:@"image" block:nil]; // returns immediately

取り出しはblocksで。
[[TMCache sharedCache] objectForKey:@"image"
                              block:^(TMCache *cache, NSString *key, id object) {
                                  UIImage *image = (UIImage *)object;
                                  NSLog(@"image scale: %f", image.scale);
                              }];
同期メソッドも用意されている。

- - - -
このライブラリはモダンでかなりいい感じ。キャッシュが非同期って言われてみれば目からウロコだった。メモリとディスク両方を使うのもユニークでいいアイディア。こうなると今までのキャッシュライブラリが古く見えてくるなー。ソースも簡潔で読みやすい(技術力ある)。


なおソースコードで気になった点が1つ。
id object = [strongSelf->_memoryCache objectForKey:key];
        if (!object) {
            object = [strongSelf->_diskCache objectForKey:key];
            [strongSelf->_memoryCache setObject:object forKey:key block:nil];
        }
strongSelf.memoryCacheではなく strongSelf->_memoryCache としているところ。ポインタアクセスでメンバ変数へアクセスしてる。単純に速度優先の為だけ???(ちなみにgetterはオーバライドされていない)。


(参考)以前書いたディスク保存型のキャッシュライブラリ。ARC非対応。

APIは同期のみ。条件が指定できてそれを超えたら勝手に消えていくところが便利。あと複数作れる(保存ディレクトリを分けられる)。
今回のライブラリと比べると古臭い...

正規表現の使える block injection

このエントリーをはてなブックマークに追加

メソッドに前後処理を加えるライブラリはいくつかあるが、このライブラリはメソッドを正規表現でまとめて対象にできるのが面白い。



引用
BlockInjectionとはなんぞや

Objective-Cの特定のメソッドの前後に処理を追加できるライブラリです。
クラスの外側からアスペクト指向的に振る舞いを追加できるのが特徴です。

「UIViewの勝手に呼ばれているsetterでまとめて全てログ出力させる」ことが3行でできる。
[BILib injectToClassWithNameRegex:BIRegex(@"^UIView$") methodNameRegex:BIRegex(@"^set.*$") preprocess:^{
  NSLog(@"%@", [BILib prettyFunction]);
}];

結果
-[UIView setContentScaleFactor:]
 -[UIView setFrame:]
 -[UIView setNeedsDisplay]
  :
  :

クラス名とメソッド名を正規表現でできるアイディアが秀逸で強力。いろいろ使いでがありそう。

CocoaDocs - ライブラリドキュメント共有サイト

このエントリーをはてなブックマークに追加

appledocで生成したクラスリファレンスが公開されている。ライブラリ名での検索もできる。



すでに大量のライブラリのドキュメントが登録されている。


ライブラリのページはこんな感じ。GitHubのREADME.mdとほぼ同じ。


クラスリファレンス


- - - -
github、CocoaPodに続いて今度はドキュメント環境も整備され、近年iOSアプリの開発環境は本当に便利なった。APIを提供してくれるとさらに便利になりそう。

Xibの多言語化作業を強力にサポートするツールとライブラリ

このエントリーをはてなブックマークに追加

iOS開発で Xibの多言語化は厄介なところだが、その作業を楽にしてくれるツールとライブラリのセット。



仕組みはこんな感じ
まずコマンドラインツールagi18nを使って .stringsファイルを生成する
xib
 ↓
agi18n実行
 ↓
xibのコントロール上のラベルを元に生成した .stringsファイル
これが各言語毎に自動的に生成される。これを各言語に合わせて翻訳しておく。
特徴的なのは、この時生成される .stringsは既に翻訳してあるところは残して、新しいものだけマージしてくれるところ。この為、開発中に何度でも実行することができる。また重複する文言をまとめてくるようだ。

プロジェクトにはツールと一緒に提供されているライブラリを追加しておく。このライブラリは実行時のxibロード時に実行環境に合わせた言語.stringsの文字をコントロールのラベルへ自動的に設定してくれる。
アプリ実行
  ↓
 xibロード
  ↓
(agi18nライブラリ).stringsファイルから文字を取得し xib上のコントロールのラベルへセット。
  ↓
 表示
NSObjectのカテゴリ内でawakeFromNibをswizzlingでフックし、そのタイミングで .stringsファイルの文字をxib上のコントロールのラベルに反映する処理を実行している。これによってアプリのコードの修正無しに文字の設定が自動的に行われる。
カスタムコントロールの場合は自分でlocalizeFromNibを実装しておけば同様に自動設定をしてくれる。作者によればこの自動設定によるオーバーヘッドは通常のxibロードにかかる時間の 5%増程度とのこと。

- - - -
Xibの多言語化方法は大別して (a)ibtoolsなどを使い言語毎にXibを作る方法 (b)新たにサポートされたXcodeのxibローカライズを使う方法 (c)コントロール全部のアウトレットを作ってNSLocalizedStringで詰め込む方法の3種類がある。自分の場合は保守性を優先して(c)を使っている。アウトレットが面倒だけれどもstringsファイルの管理だけで済むので一旦形ができれば楽。それはともかくいずれの方法も一長一短があって決定的な方法が無いのが悩ましいところ(この辺りの考察は上記の作者のページで解説されている)。実行時の自動設定というアイディアは現時点のiOS開発の多言語化方式の状況を考えると悪くはなく、そこに専用のツールをセットで提供して保守作業の負荷を軽減する考えは実用的だと思った。なお実行時に自動的にラベルをセットする方法は過去自分もやったことがありその時はかなり楽だったので案外このアイディアは悪くないと思う。

シンプルだが強力そしてエレガントなコードのシーケンサ

このエントリーをはてなブックマークに追加

ソースコードがヘッダやコメントを入れても100行にも満たない無駄の無いコード。それでいて十分な機能。



ここでいうシーケンサとは、あらかじめ登録した複数の処理を順番に実行する仕組みのこと。ネットワークアクセスやユーザインタラクションは基本非同期な処理になるが、特定の順番で同期的に処理を行いたい場合がある。シーケンサはそんな用途で利用できる。

単純な例
Sequencer *sequencer = [[Sequencer alloc] init];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSLog(@"This is the first step");
    completion(nil);
}];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSLog(@"This is another step");
    completion(nil);
}];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSLog(@"This step is going to do some async work...");
    int64_t delayInSeconds = 2.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        NSLog(@"finished the async work.");
        completion(nil);
    });
}];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSLog(@"This is the last step");
    completion(nil);
}];
[sequencer run];
completionの引数が次のステップの引数 result として渡っていく。

ネットワークアクセスの例
Sequencer *sequencer = [[Sequencer alloc] init];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
    NSURL *url = [NSURL URLWithString:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        completion(JSON);
    } failure:nil];
    [operation start];
}];
[sequencer enqueueStep:^(NSDictionary *feed, SequencerCompletion completion) {
    NSArray *data = [feed objectForKey:@"data"];
    NSDictionary *lastFeedItem = [data lastObject];
    NSString *cononicalURL = [lastFeedItem objectForKey:@"canonical_url"];

    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:cononicalURL]];
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        completion(responseObject);
    } failure:nil];
    [operation start];
}];
[sequencer enqueueStep:^(NSData *htmlData, SequencerCompletion completion) {
    NSString *html = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
    NSLog(@"HTML Page: %@", html);
    completion(nil);
}];
[sequencer run];
最初のステップでJSONを取得し、次にURL情報から HTMLを取得。最後にそれをログに表示。シーケンサを使うとデリゲートチェーンやblocksの多段ネストの必要が無くなり見通し良いコードが書ける。なかなかいい感じ。

blocksで書ける簡単スタブ

このエントリーをはてなブックマークに追加


こんな感じでblockが使える。
NSObject *stub = [Stubbilino stubObject:[[NSObject alloc] init]];
[stub stubMethod:@selector(description) withBlock:^{ return @"Stubbilino is awesome!"; }];

expect(stub.description).to.equal(@"Stubbilino is awesome!");

UITableViewControllerのスタブ例。
UITableViewController *viewcontroller = [[UITableViewController alloc] init];
UITableViewCell *myCell = [[UITableViewCell alloc] init];

UITableViewController *stub = (id)[Stubbilino stubObject:viewcontroller];

[stub stubMethod:@selector(numberOfSectionsInTableView:)
       withBlock:^{ return 1; }];

[stub stubMethod:@selector(tableView:numberOfRowsInSection:)
       withBlock:^{ return 1; }];

[stub stubMethod:@selector(tableView:cellForRowAtIndexPath:)
       withBlock:^{ return myCell; }];

[viewcontroller.tableView reloadData];

NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];

id cell = [viewcontroller.tableView cellForRowAtIndexPath:indexPath];

expect(cell).to.equal(myCell);

クラスメソッドも書ける。
Class uiimage = [Stubbilino stubClass:UIImage.class];

[uiimage stubMethod:@selector(imageNamed:)
          withBlock:^(NSString *name) { return myImage; }];

関連)



GCD利用のライトウェイトなHTTPサーバライブラリ

このエントリーをはてなブックマークに追加



Hello World例
#import "GCDWebServer.h"

int main(int argc, const char* argv[]) {
  @autoreleasepool {

    // Create server
    GCDWebServer* webServer = [[GCDWebServer alloc] init];

    // Add a handler to respond to requests on any URL
    [webServer addDefaultHandlerForMethod:@"GET"
                             requestClass:[GCDWebServerRequest class]
                             processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {

      return [GCDWebServerDataResponse responseWithHTML:@"Hello World"];

    }];

    // Use convenience method that runs server on port 8080 until SIGINT received
    [webServer runWithPort:8080];

    // Destroy server
    [webServer release];

  }
  return 0;
}

リクエストと処理のマッピングを「ハンドラ」と呼んでいていくつも登録することができる(要スレッドセーフ)。この仕組みをベースに独自の処理系が作れそう。rubyやphpの実行環境を作るのもできないことは無いかも。

ビューを録画する

このエントリーをはてなブックマークに追加

指定したビューを録画して .movファイルを作成するライブラリ。



こんな感じで使う。
Glimpse *glimpse = [[Glimpse alloc] init];

        // Start recording and tell Glimpse what to do when you are finished
        [glimpse startRecordingView:self.view withCallback:^(NSURL *fileOuputURL) {
            NSLog(@"DONE WITH OUTPUT: %@", fileOuputURL.absoluteString);
        }];
         :
     // Since our animation is complete, lets tell Glimpse to stop recording.
            [glimpse stop];
付属のデモではビュー上で実行したアニメーションの様子を録画していた。

ライブラリとしての利用の他、ソースコードも参考になる。

GCDを使った定期処理実行。ビューのスナップショット(フレーム)を撮るのに dispatch_sourceを使っている。
__weak typeof(self) weakSelf = self;
    _queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue);
    dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, 0), 0.15 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(_source, ^{

        dispatch_sync(dispatch_get_main_queue(), ^{
            UIImage *viewImage = [weakSelf imageFromView:weakSelf.sourceView];
            [weakSelf.writer writeFrameWithImage:[viewImage copy]];
        });
    });
    dispatch_resume(_source);

また GlimpseAssetWriterというクラスが用意されていて、集まったフレームを .movファイルに書きだす処理を行っている。AVAssetWriterの使い方の参考になる。

とっても便利な色のライブラリ

このエントリーをはてなブックマークに追加

さまざまなUIColor定義と便利メソッドをまとめたライブラリ。


デモ

こんな感じでたくさんの色が #define 定義されている。
// System Colors
#define ColorInfoBlue [UIColor colorWithRed:47/255.0f green:112/255.0f blue:225/255.0f alpha:1.0]
#define ColorSuccess [UIColor colorWithRed:25/255.0f green:188/255.0f blue:63/255.0f alpha:1.0]
#define ColorWarning [UIColor colorWithRed:221/255.0f green:170/255.0f blue:59/255.0f alpha:1.0]
#define ColorDanger [UIColor colorWithRed:229/255.0f green:0/255.0f blue:15/255.0f alpha:1.0]

// Whites
#define ColorAntiqueWhite [UIColor colorWithRed:250/255.0f green:235/255.0f blue:215/255.0f alpha:1.0]
#define ColorOldLace [UIColor colorWithRed:253/255.0f green:245/255.0f blue:230/255.0f alpha:1.0]
#define ColorIvory [UIColor colorWithRed:255/255.0f green:255/255.0f blue:240/255.0f alpha:1.0]
  :

色に関する便利メソッド
// Color Methods
+(UIColor *)colorFromHex:(NSString *)hexString;
+(NSString *)hexFromColor:(UIColor *)color;
+(NSArray *)rgbaArrayFromColor:(UIColor *)color;
+(NSArray *)hsbaArrayFromColor:(UIColor *)color;

中でも面白いのがカラーバリエーションを選んでくれるメソッド。
+(NSArray *)generateColorSchemeFromColor:(UIColor *)color ofType:(ColorScheme)type;
タイプは4種類
// Color Scheme Creation Enum
typedef enum {
    ColorSchemeAnalagous = 0,
    ColorSchemeMonochromatic,
    ColorSchemeTriad,
    ColorSchemeComplementary
}ColorScheme;
1つ色を指定すると、タイプに応じた色のバリエーションを配列を返してくれる。これはいいかも。

永続化されたジョブキューライブラリ

このエントリーをはてなブックマークに追加

キューの内容をSQLiteを使い保存。登録したジョブはGCDでバックグラウンド実行される。



APIはこんな感じ。enqueueWithData:forTask:でジョブ(処理)をキューへ詰めていく。
EDQueue.h

@interface EDQueue : NSObject

@property (weak) id delegate;
@property (readonly) Boolean isRunning;
@property (readonly) Boolean isActive;
@property NSUInteger retryLimit;

+ (EDQueue *)sharedInstance;
- (void)enqueueWithData:(id)data forTask:(NSString *)task;
- (void)start;
- (void)stop;
@end


ジョブ処理例はこんな感じ。
- (EDQueueResult)queue:(EDQueue *)queue processJob:(NSDictionary *)job
{
    sleep(1);           // This won't block the main thread. Yay!

    // Wrap your job processing in a try-catch. Always use protection!
    @try {
        if ([[job objectForKey:@"task"] isEqualToString:@"success"]) {
            return EDQueueResultSuccess;
        } else if ([[job objectForKey:@"task"] isEqualToString:@"fail"]) {
            return EDQueueResultFail;
        }
    }
    @catch (NSException *exception) {
        return EDQueueResultCritical;
    }

    return EDQueueResultCritical;
}
delegateを設定しておくと上記メソッドが別スレッドで呼び出される。

アプリ終了後も処理を記憶して、次回起動時にも処理を継続したいケースに使える。


ちなみに拙作でこんなのもあります。

キューの管理だけに特化しいるので、出し入れは呼び出し側で行う必要はある。こっちも永続化あり(SQLite未使用)。UITableViewなどGUIとの連携を想定していてエントリ毎の細かい情報が取れたりする。
※ドキュメントなし&非ARC

NSLogでblocksのシグネチャ出力を可能にする

このエントリーをはてなブックマークに追加

NSBlockの+descriptionを method_setImplementation()でオーバーライドしてblocksのシグネチャを返すライブラリ。

こういう blocksがあったとする。

NSString * (^someBlock)(NSString *, BOOL, CGRect, float*[30]) = ^(NSString *a, BOOL b, CGRect c, float *d[30]) {
    return @"Some return value";
};
NSLog(@"This is my block! %@", someBlock);

普通NSLogの出力は名前とアドレスしか表示されない。
This is my block! <__NSGlobalBlock__: 0x35c0>

このライブラリを入れるとblocks関数の引数を含むシグネチャが表示できるようになる。
This is my block! <__NSGlobalBlock__: (id (^)(id, char, struct CGRect, float*[30]))>
デバッグ専用。

使う場面は限られてくる一方、NSBlockをカテゴリ拡張したソースコードが興味深い。blocksのデータ構造を知るのに役立つかも。
__attribute__((constructor)) を使って初期化するのか。ふむふむ。

(参考)c++ - How exactly does __attribute__((constructor)) work? - Stack Overflow
http://stackoverflow.com/questions/2053029/how-exactly-does-attribute-constructor-work
1. It's run when a shared library is loaded, typically during program startup.
2. That's how all GCC attributes are; presumably to distinguish them from function calls.
3. GCC-specific syntax.
:





チュートリアル画面作成支援

このエントリーをはてなブックマークに追加

初回に表示するチュートリアル画面の作成を支援するライブラリ。



作り方は簡単。必要な画面分だけMyIntroductionPanelのインスタンスを作る。その時に画像とメッセージを指定しておく。
MYIntroductionPanel *panel = [[MYIntroductionPanel alloc] initWithimage:[UIImage imageNamed:@"SampleImage1"] description:@"asd asd a sdfffdasfas fasd fasdf sd fsdfdsfasd asdfdfsdfa asd fdsffdsfa  asdf asdffds asd adfsdfdfasf df"];
MYIntroductionPanel *panel2 = [[MYIntroductionPanel alloc] initWithimage:[UIImage imageNamed:@"SampleImage2"] description:@"asd asd a sdfffdasfas fasd fasdf sd fsdfdsfasd asdfdfsdfa asd fdsffdsfa  asdf asdffds asd adfsdfd fasf df aljdsfljasdlkfj laksjdfl sljd flajsdjjd sfkjd kkdfkj dkjfj asd asdfasdf  asd s s asasdff dsdafdfa asdfss sddds a s ddsafdasdf asd sd as sdadsad  asdsds a sdfssfd"];
そして MyIntroductionViewを作り、表示するだけ。
MYIntroductionView *introductionView = [[MYIntroductionView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) headerImage:[UIImage imageNamed:@"SampleHeaderImage.png"] panels:@[panel, panel2]];
[introductionView showInView:self.view];


サンプル


右フリックでめくっていける。

作る手間が省けて地味に便利。

アドホックなP2Pネットワークフレームワーク

このエントリーをはてなブックマークに追加

iPhone同士や、iPhoneとMacの間でアドホックなP2Pネットワークを作り、通信を可能にするフレームワーク。


デバイスの探索から通信の管理までひと通りのことが行える。

Publish Subscribeコマンド

一斉送信ができるっぽい。

Pulbisher側のコード例
#import "threeMF.h"
self.tmf = [TMFConnector new];

// Provide
self.kvCmd = [TMFKeyValueCommand new];[self.tmf publishCommand:self.kvCmd];

// Execute
TMFKeyValueCommandArguments *kvArguments = [TMFKeyValueCommandArguments new];
kvArguments.key = @"msg";
kvArguments.value = @"Hello World!";
[self.kvCmd sendWithArguments:kvArguments];

Subscribe側のコード例
#import "threeMF.h"
self.tmf = [TMFConnector new];

// Discover
[self.tmf startDiscoveryWithCapabilities:@[ [TMFKeyValueCommand name] ] delegate:self];

// Subscribe
- (void)threeMF:(TMFConnector *)tmf didChangeDiscoveringPeer:(TMFPeer *)peer forChangeType:(TMFPeerChangeType)type {
            if(type == TMFPeerChangeFound) {
                 [self.tmf subscribe:[TMFKeyValueCommand name] peer:peer receive:^(TMFKeyValueCommandArguments *arguments){
                 // do awesome things
                 NSLog(@"%@: %@", arguments.key, arguments.value);
            }
            completion:^(NSError *error){
                 if(error) { // handle error
                     NSLog(@"%@", error);
                 }
            }];
          }
      }

Respons Request コマンド

いわゆるRPC的な使い方。
CADAnnounceCommandArguments *args = [CADAnnounceCommandArguments new];
 args.name = self.nameLabel.text;
 args.color = _color;

[self.tmf sendCommand:[CADAnnounceCommand class] arguments:args destination:peer
 response:^(id response, NSError *error){
 // handle response or error
 }];


付属のサンプルプログラムをiPhone5とMacBookProでそれぞれ実行するとMacのスクリーンショットがiPhoneに転送された。

iPhoneの画面


Macの画面





- - - -
高機能にもかかわらず使い勝手が良さそう。


Javaでアプリ開発

このエントリーをはてなブックマークに追加



RoboVM がJavaのバイトコードを ARM/x86のネイティブコードに変換するのでVMが不要とのこと。


Objective-C bridgeにより、JavaからObj-Cオブジェクトが扱える。UIKit対応済、とのこと。


UIApplicationDelegateはこんな感じ。
import org.robovm.cocoatouch.coregraphics.*;
import org.robovm.cocoatouch.foundation.*;
import org.robovm.cocoatouch.uikit.*;

public class IOSDemo extends UIApplicationDelegate.Adapter {

    private UIWindow window = null;
    private int clickCount = 0;

    @Override
    public boolean didFinishLaunching(UIApplication application,
            NSDictionary launchOptions) {
  
        final UIButton button = UIButton.fromType(UIButtonType.RoundedRect);
        button.setFrame(new CGRect(115.0f, 121.0f, 91.0f, 37.0f));
        button.setTitle("Click me!", UIControlState.Normal);

        button.addOnTouchUpInsideListener(new UIControl.OnTouchUpInsideListener() {
            @Override
            public void onTouchUpInside(UIControl control, UIEvent event) {
                button.setTitle("Click #" + (++clickCount), UIControlState.Normal);
            }
        });

        window = new UIWindow(UIScreen.getMainScreen().getBounds());
        window.setBackgroundColor(UIColor.lightGrayColor());
        window.addSubview(button);
        window.makeKeyAndVisible();
        
        return true;
    }

    public static void main(String[] args) {
        NSAutoreleasePool pool = new NSAutoreleasePool();
        UIApplication.main(args, null, IOSDemo.class);
        pool.drain();
    }
}
ヲヲ。。

ライセンス:GPLv2

関連情報