Objective-C スレッドと libuv スレッドとのやりとり

libuv 専用スレッドと通常の Objective-C スレッドとのやりとりの仕方、ついでに書いておく。

tl;dr - libuv スレッドから Objective-C スレッドに対して何か送るときは Objective-C の作法がそのまま使える。逆は uv_async を使う

libuv スレッドから Objective-C スレッド

これは Objective-C の世界の作法がそのまま使える。

iOS4+ と OSX 10.6+ であれば GCD で、

dispatch_async(dispatch_get_main_queue(), ^{
    // ここはメインスレッド
});

のようなのを書くだけでメインスレッドの動作を定義できるから、そこでメソッド呼ぶなり Notification を発行するなりすれば良いので楽ちん。

それ以下の環境だったら performSelector:onThread:withObject:waitUntilDone: 系のを使う。

Objective-C スレッドから libuv スレッド

libuv スレッドでは NSRunLoop が回ってないので上記の作法は使えない。代わりに uv_async を使う。

まず、libuv スレッドで async コールバックを設定:

static void async_cb(uv_async_t* handle, int status) {

}

uv_async_init(self->loop, &self->async, async_cb);

で、呼び出し側(Objective-Cスレッド)から

uv_async_send(&obj->async);

とすれば async_cb が libuv スレッドで発動するという寸法。

データを渡したいときは

@synchronized (obj.send_queue) {
    [obj.send_queue addObject:@"foo"];
}

uv_async_send(&obj->async);

という感じにしておいて、取り出す側も

static void async_cb(uv_async_t* handle, int status) {
    NSArray* queue;

    @synchronized (self.send_queue) {
        queue = [NSArray arrayWithArray:self.send_queue];
        [self.send_queue removeAllObjects];
    }

    // 処理
}

みたいにすれば良い。

async コールバックは複数作ることができるから、用途に応じてコールバックを使い分けるのがよさそう。(データ送信用、スレッド終了用など)

by typester / at 2012-01-24T14:12:00 / objc · libuv / Comment

libuv (libev) と Objective-C autorelease のはまりポイント

iOS や Mac アプリで HTTP 以外のネットワーク機能をつけたいといった場合に、libuv や libev を組み込んで使うというのを割とよくする。方法としては以下のような感じでその機能用のスレッドをつくる:

-(void)run {
    NSThread* thread = [[NSThread alloc] initWithTarget:self
                                               selector:@selector(loop)
                                                 object:nil];
    self.thread = thread;
    [thread release];

    [thread start];
}

スレッドの中身は大体こんな感じ:

-(void)loop {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    uv_loop_t* loop = uv_loop_new();

    // いろいろ初期化
    // ...

    // libuv イベントループ
    uv_run(loop);

    uv_loop_delete(loop);

    [pool drain];
}

このスレッドは uv_run でブロックしてしまう。本来ここではCocoaのイベントループ(NSRunLoop)をまわす部分だが、かわりに libuv のイベントループを回している感じになっている。

したがってこのスレッドで Objective-C を混ぜる場合には autorelease がスレッド終了まで基本的にされなくなるから注意が必要。

これを解決する方法で最初に思いつくのは、uv_run (ev_run) のかわりに uv_run_once (ev_run(..., EVRUN_ONCE)) を使うことだ。

while (1) {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    uv_run_once(loop);
    [pool drain];
}

これは一見簡単だけど while ループをとめるフラグを別途用意しないといけないし、uv_run とちがってループを抜けるときには各イベントハンドラが終了しているかを確かめる必要もありめんどくさい。

結果いまはこんな感じにしている:

static void idle_cb(uv_idle_t* handle, int status) {
    uv_idle_stop(handle);
    [(NSAutoreleasePool*)handle->data drain];
    handle->data = NULL;
}

static void check_cb(uv_check_t* handle, int status) {
    uv_idle_t* idle = (uv_idle_t*)handle->data;
    if (NULL != idle->data) return;

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    idle->data = (void*)pool;
    uv_idle_start(idle, idle_cb);
}

-(void)loop {
    uv_loop_t* loop = uv_loop_new();

    uv_check_t check;
    uv_check_init(loop, &check);
    uv_check_start(&check, check_cb);

    uv_idle_t idle;
    uv_idle_init(loop, &idle);
    check.data = (void*)&idle;
    idle.data = NULL;

    // いろいろ初期化
    // ...

    uv_run(loop);

    uv_loop_delete(loop);
}

uv_check (ev_check) で NSAutoreleasePool をつくりつつ idle タイマーを作って、 その idle タイマー時に [pool drain] する。 これだとイベントが詰まっている場合は drain は呼ばれず、キリの良いときに呼んでくれるから run_once でいちいちやるよりは効率も良さそうな気がする。

このコードでは省略しているけど実際に使うときには checkidle も終了しないと uv_run から抜けてこないのでどこかのオブジェクトにまとめて突っ込んでおいたりして使うのが吉。

こういうのを何も考えずに Objective-C をまぜるともりもりメモリ食うようになるから気をつけよう!

by typester / at 2012-01-24T13:06:00 / objc · libuv / Comment

Objective-C で AUTOLOAD (あるいは method_missing )

forwardInvocation とかを使えば出来るみたい。

#import <Foundation/Foundation.h>

@interface Foo : NSObject;
-(void)call:(NSString*)sel;
@end

@implementation Foo

-(void)call:(NSString*)sel {
    NSLog(@"call: %@", sel);
}

-(void)forwardInvocation:(NSInvocation *)invocation {
    [self call:NSStringFromSelector([invocation selector])];
}

-(NSMethodSignature*)methodSignatureForSelector:(SEL)sel {
    NSMethodSignature* sig = [super methodSignatureForSelector:sel];
    if (sig) return sig;

    return [[self class] instanceMethodSignatureForSelector:@selector(call:)];
}

@end

int main() {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    Foo* foo = [[Foo alloc] init];

    [foo bar];
    [foo buzz];

    [foo release];

    [pool drain];
    return 0;
}

// gcc -framework Foundation foo.m

詳細は Objective-C Runtime Programming Guide にある。

by typester / at 2011-12-05T20:32:00 / iphone · objc / Comment

PerlのXSでObjective-Cを使うときに気をつけること

QuickDraw API で定義されている Move マクロが、perl の handy.h の中で定義されているものとコンフリクトするため、

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"

// undefine Move macro, this is conflict to Mac OS X QuickDraw API.
#undef Move

#import <Foundation/Foundation.h>

こんなようにしてperl関係のヘッダーをincludeしたあと#undefしてあげたうえでCocoaのヘッダーをimportするようにすると良い。

なお、 Foundation 以外にリンクするときはまた別の問題があるかもしれない。

基本XS使うときは、まず単体でうごくCやObjective-Cのコードを書いて、XSはそれをつなぐだけという感じで使っているので、ガチでXS内でごりごり処理を実装することはなく、perlのMoveマクロは使ったことないから、それを使いたいときにどうすればいいかは知らない。

memmoveのラッパーみたいだけど、NewxやSafefreeなどのようにこれ使ったほうが良いとかたぶんあるんだろうけど。

by typester / at 2010-12-31T01:46:00 / perl · xs · objc / Comment

PerlエンジニアのためのObjective-C Blocks入門

OS X 10.6 以降の xcode では Objective-C に Blocks というシンタックスが追加されている。

Blocks Programming Topics: Introduction

Perl でいうところの無名関数(コードブロック)を作ることができる機能で、Perlでいうところの

my $f = sub { ... };
$f->();

void (^f)() = ^{ ... };
f();

のように書ける。書式がきもいのはObjective-Cの定めなのであきらめましょう。 より詳しい書式については上記ドキュメントを見ると良い。

しかしデフォルトではPerlのコードブロックとはレキシカル変数の扱いが異なる。

my $i = 0;
my $f = sub { return $i + 1 };

$i++;

$f->(); # 2 を返す

perlではこのようにレキシカル変数はコードブロック内と共有されるが、Objective-Cの場合は

int i = 0;
int (^f)() = ^{ return i + 1; };

i++;

f(); // 1 を返す

このようになる。これはブロックを作成するときのレキシカル変数がコピーされるからである。 Perlとおなじような挙動を望む場合は __block ストレージタイプを指定すればいいようだ。

__block int i = 0;
int (^f)() = ^{ return i + 1; };

i++;

f(); // 2

ここまでわかればObjective-CでBlockの再帰を書くことができる。

__block void (^f)();
f = ^{ f(); };
f();

これは以下とおなじ、

my $f;
$f = sub { $f->() };
$f->();

というようにPerlエンジニアにとっては割と直感的なコードブロックが使えるようになっております。 書式がきもいことをのぞけばいい感じです。

次回は「PerlエンジニアのためのGrand Central Dispatch」の予定です。

追記@2010-05-11T19:12:48+09:00

はてなブックマーク - PerlエンジニアのためのObjetive-C Blocks - unknownplace.org

lyokato 「Objective-Cの仕様というよりは、Blocks拡張に対応したCコンパイラの仕様かな」

ってことでやってみたら

#include <stdio.h>

int main(int argc, char** argv) {
    void (^f)() = ^{ printf("Hello Blocks!\n"); };
    f();

    return 0;
}

// $ gcc foo.c
// $ ./a.out

でも行けました。gcc の拡張なんですね。あざっす!

by typester / at 2010-05-11T14:29:00 / objc · perl / Comment