Powered By Blogger

2012年3月24日土曜日

Objective-C++でboostを使う時のリンクエラー

XCodeでObjective-C++でboostを使う時に以下のリンクエラーが出た場合。

Undefined symbols for architecture x86_64:
"boost::system::generic_category()"

boostの.dylibが足りないらしい。
libboost_system-mt.dylib をTARGETSのBuild PhaseのLink Binary With Librariesに追加する。

2012年3月23日金曜日

Objective-cでコルーチン?!

「C10K問題を回避するためにコルーチンを使う方法」というブログ記事があって、なるほどと思いObj-cに移植しようとしたのですが、どうもコルーチンクラスをサンプルに特化させていじっている様で汎用性が無さそうであまり使い勝手よくありません。

仕方ないので元記事のソースはあまり参考にせずに移植と言うより作ってみました。
→ここgithub

C10K問題を回避するためには、コネクション数とスレッド数が比例しないような設計をしなければならない事からコルーチンが有効らしいです。
実際に通信するコードを書ければ素晴らしいですが、面倒くさいので省いてコルーチンの動作を確認するだけです。

まぁObj-cのコルーチンはCSCoroutineとかあるみたいですが、setjmp, longjmp使ってるのが気持ち悪かったりします。(食わず嫌い)

yieldではsetjmp, longjmp使わず普通にreturnで抜けているのでローカル変数等は保存されません。
なのでローカル変数の代わりにメンバ関数を使っています。
そういう作りなのでコルーチンからサブコルーチンを呼ぶ時には新たなコルーチンオブジェクトを作成します。
色々と制約あって万能ではありませんが、ちょこっとコルーチンを使いたいぐらいなら丁度よいかも。

使用例
二組のコルーチンを使って擬似マルチスレッドをシングルスレッドで実現しています。
performメソッドがコルーチンの中身ですが、COR_YIELDでmainへ値を戻し、mainで次にnextメソッド呼ぶとCOR_YIELDの直後から続き実行します。

//
#import "TestCoroutine.h"
#import "CoroutineX.h"


@interface TestCoroutine (/*PRIVATE*/)
- (NSString*)makeChildName;
@end

@implementation TestCoroutine {
  int _concurrency_level;
  NSString *_name;

  int _cl, _i, _j;
  NSString *_retStr;
}

- (id)initWithConcurrencyLevel:(int)lv name:(NSString*)name {
  self = [super init];
  if (self) {
    _concurrency_level = lv;
    _name = name;
  }
  return self;
}


- (NSString*)makeChildName {
  static int c =0;
  return [NSString stringWithFormat:@"%@c%d", _name, c++];
}

- (id)perform {
  /*
   You can NOT use any local variables in this method.
   */
  static int forkDepth = 2;
  COR_BEGIN;

  for (_cl = 0; _cl < _concurrency_level; ++_cl) {
    //recursive call
    COR_FORK([[TestCoroutine alloc] initWithConcurrencyLevel:forkDepth-- name:[self makeChildName]]);        
  }
  
  for (_i=0; _i < 3; ++_i) {
    _retStr = [NSString stringWithFormat:@"%@'s loop1 i=%d",self->_name, _i];
    COR_YIELD(_retStr);
  }
  
  for (_i=0; _i < 2; ++_i) {
    for (_j=0; _j < 3; ++_j) {
      _retStr = [NSString stringWithFormat:@"%@'s loop2 i=%d, j=%d",self->_name, _i, _j];
      COR_YIELD(_retStr);
    }
  }

  //sub coroutine call
  COR_FORK([[SubCoroutine alloc] init]);

  COR_END;
}

@end


@interface SubCoroutine (/*PRIVATE*/)
@end

@implementation SubCoroutine

- (id)perform {
  /*
   You can NOT use any local variables in this method.
   */
  COR_BEGIN;

  COR_YIELD([NSNumber numberWithFloat:1.2]);
  COR_YIELD([NSNumber numberWithFloat:2.3]);

  COR_END;
}

@end


//
//  main.m
//

#import 
#import "TestCoroutine.h"


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

  @autoreleasepool {

    CoroutineState *corState1 = [[CoroutineState alloc] init];
    TestCoroutine *coro1 = [[TestCoroutine alloc] initWithConcurrencyLevel:3 name:@"p0"];
    [corState1 push:coro1];

    CoroutineState *corState2 = [[CoroutineState alloc] init];
    TestCoroutine *coro2 = [[TestCoroutine alloc] initWithConcurrencyLevel:2 name:@"p1"];
    [corState2 push:coro2];

    id retVal1, retVal2;
    do {
      retVal1 = [corState1 next];
      retVal2 = [corState2 next];
      NSLog(@"1:%@, 2:%@", retVal1, retVal2);
      NSLog(@"1's stack-size=%ld, 2's stack-size=%ld", [corState1 stackSize], [corState2 stackSize]);
    } while (retVal1 || retVal2);

  }
    return 0;
}

2012年3月5日月曜日

iOS,ARC:weakポインタを使う場合の注意

うっかり循環強参照(循環参照では無い)にしてしまうとオブジェクトが解放されずリークとなりますので、
弱い参照を使って循環強参照を防ぎます。

ただし、weak修飾子は iOS5以降でしか利用できないので、iOS4ではunsafe_unretainedをweak修飾子の代わりとします。
ところがunsafe_unretainedは挙動が素直と言うかweakほど気が利かないので、iOS5/ARCに慣れてしまうとiOS4/ARCで痛い目を見ると言う話です。

詳細はこの元ネタ参照。
元ネタはObj-cお馴染みのデリゲートパターンでは循環参照になるので、これが循環強参照とならない様にdelegateの属性を弱参照とするのだが、うっかりiOS4/ARCで痛い目を見たという話です。

weak属性が気が利いてるというのは、例えばこの本によると、
コンパイラは、__weak修飾子付きの変数をアクセスする場合、必ずautoreleasepoolに登録してからアクセスする様なコードを生成するらしい。
//
id __weak obj1 = obj0;
NSLog(@"class=%@", [obj1 class]);

//
id __weak obj1 = obj0;
id __autoreleasing tmp = obj1;
NSLog(@"class=%@", [tmp class]);
と等価なコードに生成するそうです。

わざわざこんなコードを生成する理由は元ネタを読むと分かるので説明は省略します。

●対策
元ネタにも対策が書いてあるのですが、問題が発覚してからの対処の様な感じでちょっと分かりにくいと感じるかもしれません。
ここは敢えて別の方法を考えてみましょう。
要はレシーバが弱参照の場合は__weakの時と同じ挙動をする様にすれば良いのです。

こんなマクロ関数(WeakSendMessage)を作ります。
//
//1:弱参照にweakを使う。0:弱参照にunsafe_unretainedを使う
#define kUsingWeak 0

#if kUsingWeak
  #define unretained   weak
  #define __unretained __weak
  #define WeakSendMessage(_r, _s, _a1) \
         [(_r) _s(_a1)];
#else
  #define unretained   unsafe_unretained
  #define __unretained __unsafe_unretained
  #define WeakSendMessage(_r, _s, _a1) \
         __autoreleasing NSObject *tmp = (_r); \
         [tmp performSelector:@selector(_s) withObject:(_a1)];
#endif
いったん__autoreleasing属性の一時ポインタに置き換えてます。

この様にレシーバが弱参照のメッセージングの箇所を
//
  //self.delegateは弱参照
  [self.delegate callFromClassB:self];
こう変えます。
//
  //self.delegateは弱参照
  WeakSendMessage(self.delegate, callFromClassB:, self);

これでiOS4でもiOS5でも同じ挙動になるはず。

と言うか、循環参照するならiOS4は切り捨てろ。
iOS4を切り捨てられないなら循環参照はするな、というのが正しいのかもしれません。

因みにWeakSendMessageの一時ポインタの属性__autoreleasingを__strongに変えても問題無く動作します。
//
  #define WeakSendMessage(_r, _s, _a1) \
         __strong NSObject *tmp = (_r); \
         [tmp performSelector:@selector(_s) withObject:(_a1)];
この方法はC++0x(あるいはboost)のスマートポインタweak_ptrと考え方がだいたい似ていると思われます。
弱参照weak_ptrからは直接参照先にアクセス出来なくて、メンバ関数lock()で強参照を取り出す必要があります。
こんな感じ
//弱参照から強参照を取り出している
weakPtr.lock()->doIt();
//weakPtr->doIt(); //これはコンパイルエラー
weak_ptrは使った事が無いので、確信はありませんが…

2012年3月4日日曜日

iOS:TableViewのセクションヘッダにUITextFieldを追加する

UITableViewControllerに以下を追加する。
*iOS5 ARCです。MRCの場合は少しコードを変える必要があります。
//
static const CGFloat kHeight = 40.0f;

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
  UIView *v = [[UIView alloc] init];
  UITextField *txtF = [[UITextField alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 160.0f, kHeight)];
  v.backgroundColor = [UIColor brownColor];
  txtF.textColor = [UIColor yellowColor];
  [v addSubview:txtF];
  return v;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  return kHeight;
}