Powered By Blogger

2014年6月30日月曜日

MacからSakura VPSをセットアップ

備忘録.
さくらVPSはデフォルトでrootログインだったりで色々不安なので変更が必要。
下の元ネタを参考にさせて頂きました。
元ネタ1
元ネタ2

1. MacでRSA暗号鍵の生成
Macで鍵のペアを生成する
$ cd .ssh/
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/***/.ssh/id_rsa): id_rsa_sakura
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in id_rsa_sakura.
Your public key has been saved in id_rsa_sakura.pub.
The key fingerprint is:
........
The key's randomart image is:
+--[ RSA 2048]----+
|     .           |
..........
+-----------------+

2. ユーザー作成
ターミナルを起動してsshでサーバにログイン
ssh root@[IPアドレス] -p 22
ログイン用のユーザー(名前は自由)を作成。
# useradd [USER名] -G wheel
# passed [USER名]

3. yumのアップデート
なぜか初期状態ではyumがupdateしてくれないので、
# vi /etc/yum.repos.d/CentOS-Base.repo
[base]
name=CentOS-$releasever - Base
#mirrorlist=http://mirrorlist.centos.org/?release=$releasever&arch=$basearch&repo=os
baseurl=http://ftp.riken.jp/Linux/centos/$releasever/os/$basearch/
#baseurl=http://mirror.centos.org/centos/$releasever/os/$basearch/
gpgcheck=1
gpgkey=http://mirror.centos.org/centos/RPM-GPG-KEY-centos4
priority=1
protect=1
以下、各リポジトリでmirrorlistをコメントアウトし、理研のサイトをbaseurlに記述。
# yum clean all
# sudo yum update

4. sudoの設定
root権限を使えるwheelグループについて設定
sudo vi /etc/pam.d/su
でファイルを開いて以下の行のコメントアウトを外す。
# auth required pam_wheel.so use_uid
sudo vi /etc/login.defs に以下の行を追加
SU_WHEEL_ONLY yes
sudo visudoで以下の行のコメントアウトを外す
# %wheel ALL=(ALL) ALL

セキュリティを強化するためにSSHのポート番号とrootでのログインを禁止。(sshd再起動後反映)
sudo vi /etc/ssh/sshd_config で以下のように修正
Port [任意のport no]
PermitRootLogin no
PasswordAuthentication no

5. scpで公開鍵の送信
Mac側からscp接続をして生成した公開鍵を送る
$ scp id_rsa_[VPS名].pub root@[IPアドレス]:/home/[USER名]/.ssh/authorized_keys

サーバに公開鍵を送信したら、authorized_keysの所有者とパーミッションを変更してサーバ側のsshdを再起動
$ ssh root@[IPアドレス] -p 22
# su - USER_NAME
# sudo chown [USER名]:[USER名] .ssh/authorized_keys
# chmod 600 /home/[USER名]/.ssh/authorized_keys
# exit #<= rootに戻る
# /etc/init.d/sshd restart
# exit

6. Mac側で簡単にログイン出来るようにSSHのconfigに設定しておく
Mac側で .ssh の config に、以下を追加しておく。
Host sakura
 User [ユーザー名]
 Port [上で設定したSSHのポート]
 Hostname [さくらのIP]
 IdentityFile ~/.ssh/id_rsa_sakura
 TCPKeepAlive yes
 IdentitiesOnly yes

2014年6月15日日曜日

BloggerにTeXの数式を貼る

以前から使ってたやつが色々使いにくいので、MathJaxを試してみる。
元ネタ
テスト: $e^{i\pi}=-1$
んー…iPhoneではSafariもChromeも変換しないな。
PuffinならiPhoneでも表示される。
数式はMathJaxを使うことが一般的らしいのだが

2013年5月3日金曜日

REPLのラッパー

備忘録

Guile(schemeの処理系)のインタープリタも↑↓のコマンド履歴とか対応してなくて使いにくい。
履歴遡ろうと↑とか押しても

> guile

scheme@(guile-user)> ^[[A^[[A^[[1;2D

みたいな事になる。

rlwrapを使えばOK。
インストールはubuntuではaptitudeで出来た。
windowsのcygwinでもsetupでrlwrapで検索しインストール出来る。
インストールの詳細は省く。簡単に分かるはず。

使い方は簡単
起動したいインタープリタのコマンドの前にrlwrapを付けるだけ。

> rlwrap guile

2012年6月15日金曜日

Mac: Clojureコンソール(clj)を使いやすくする

Clojureはコンソールからcljで起動出来る。

$ clj
Clojure 1.4.0
user=>

これが↑↓キーで入力履歴が出なかったりしてイマイチ使いづらい。
Clojureのビルドツールleiningenを使って起動すると↑↓キーで入力履歴が出る様になる。
ちょっとClosureを試してみるだけなのでビルドツールは要らないと思っていたが、
入力履歴が使えないのは不便なのでleiningenを使ってcljを起動してみると
入力履歴が使える様になった。
因みにClojureもleiningenもhomebrewからインストール出来る。

$ lein repl
REPL started; server listening on localhost port
user=> 

2012年6月4日月曜日

ペアノ算術とProlog

ペアノ算術をPrologで表現してみる。

元ネタ
第3巻『数学ガール/ゲーデルの不完全性定理』
Prolog by Example: Peano Arithmetics

Swi-Prologを使います。

と…その前にペアノの公理で自然数を定義しよう。

ペアノの公理1
0は自然数である



Prologコード
natural_n(0).

ペアノの公理2
どんな自然数nに対しても後続数s(n)は自然数である



Prologコード
natural_n(s(N)) :- natural_n(N).

ペアノの公理3
どんな自然数nに対してもs(n)は0ではない



Prologコードは無し

ペアノの公理4
どんな自然数m,nに対しても、s(m)=s(n)ならばm=nである



Prologコードは無し
ペアノの公理5
自然数nに対する述語P(n)で、次の2つが成り立つとする。
・P(0)である。
・どんな自然数kに対しても、P(k)ならばP(s(k))である。
このときどんな自然数nに対してもP(n)が成り立つ。



Prologコードは無し
ここまでのPrologコードまとめ
natural_n(0).
natural_n(s(N)) :- natural_n(N).

実行してみる。
?- natural_n(X).
X = 0 ;
X = s(0) ;
X = s(s(0)) ;
X = s(s(s(0))) ;
X = s(s(s(s(0)))) ;
X = s(s(s(s(s(0))))) .

自然数が出来た!
「これが自然数?」と言いたい人もいるでしょうが、これで良いんです!!
s(0)を1
s(s(0))を2
s(s(s(0)))を3
というふうに読み替えて下さい。


さて、ここからがペアノ算術

足し算の公理1
どんな自然数nに対しても、n + 0 = nが成り立つ


Prologコード
sum(N,0,N).

余談ですが自然数に0を含める考え方(0,1,2 ...)と含めない考え方(1,2,3 ...)があり、上の足し算の公理は前者です。
後者の場合の公理はこうなります。
足し算の公理1(0は自然数に含めない)
どんな自然数nに対しても、n + 1 = s(n)が成り立つ


"0"という自然数は存在しないので、こうするしかないのでしょう。
この場合は、
s(1)を2
s(s(1))を3
s(s(s(1)))を4
というふうに読み替えて下さい。

Prologコード
sum(N,1,s(N)).

足し算の公理2



Prologコード
sum(N,s(M),s(K)) :- sum(N,M,K).

Prologコードまとめ
sum(N,0,N).     %In case of natural numbers contain 0.
sum(N,1,s(N)).  %In case of natural numbers contain NO 0.
sum(N,s(M),s(K)) :- sum(N,M,K).  %K=N+M

X=3+2を実行してみます。

?- sum(s(s(s(0))),s(s(0)),X).
X = s(s(s(s(s(0))))) ;
false.

X=5になりました。
おっと、ここで作った自然数は"5"とは言わないんでしたね。
s(s(s(s(s(0)))))ですね、でも長ったらしいので5と言う事にします。

因みに、0は自然数に含めない場合で
X=4+3を実行してみます。

?- sum(s(s(s(1))),s(s(1)),X).
X = s(s(s(s(s(s(1)))))) ;
false.

X=7になりました。

0を自然数に含める場合は、例えば2をs(s(0))と表現すると
sum(N,s(M),s(K)) :- sum(N,M,K).
を再帰的に評価し、最終的に
sum(N,0,N).
にマッチして、再帰の階段を戻って結果が出る訳です。

0を自然数に含めない場合は、例えば2をs(1)と表現して、最終的に
sum(N,1,s(N)).
の方にマッチするのです。

ではここからは掛け算です。
掛け算の公理1
0を自然数に含める場合

Prologコード
prod(M,0,0).

0を自然数に含めない場合

Prologコード
prod(M,1,M).

掛け算の公理2



Prologコード
prod(M,s(N),P) :-
     prod(M,N,K),
     sum(K,M,P).   % K=M+N

Prologコードまとめ
prod(M,0,0).    %In case of natural numbers contain 0.
prod(M,1,M).    %In case of natural numbers contain NO 0.
prod(M,s(N),P) :-
    prod(M,N,K),
    sum(K,M,P).   % K=M+N

実行

0を自然数に含める場合、X=2*3

?- prod(s(s(0)),s(s(s(0))),X).
X = s(s(s(s(s(s(0)))))) ;
false.

X=6となりました。


0を自然数に含めない場合、X=2*4

?- prod(s(1),s(s(s(1))),X).
X = s(s(s(s(s(s(s(1))))))) ;
false.

X=8となりました。

2012年4月21日土曜日

Blocks関連のバグがデバッグコンパイルで再現しないケース

TOMOHISAさんのブログ記事
BLOCKSを使った記述で、リリースビルドのみにクラッシュする事例
これは元のコードの問題はスタック領域上のブロックオブジェクトをスコープ外で使ってしまったという割と有りがちなバグだったのですが、 念のため検証してみました。

ところがブロックオブジェクトのデフォルトの生成場所(スタック領域かヒープ領域か)がデバッグコンパイル時にはどうも違うらしくて、 そういう挙動を知らずにデバッグコンパイルで検証してしまった為にハマってしまいました。

今回のはちゃんとリリースコンパイルでもテストしないといけないという教訓です。

ブロックのデフォルトの生成場所はスタック領域なのですが、デバッグだろうがリリースだろうがそれは同じだと思い込んでいました。

特に__block属性付き変数をキャプチャするとブロックの生成場所は最初からヒープになるみたいです。何でかな?…

検証コード
コードはTOMOHISAさんのものですが、
NSLog(〜〜,block) の箇所とコメントは私が検証の為に追加しました。
因みに_tweetはこのコードが属するオブジェクトのメンバ関数です。
ARCです。
//
    NSMutableArray *arrRows = [NSMutableArray arrayWithCapacity:0];
    { //検証コード1
#warning this code only crash on Release Build.... Don't use this
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 1 Crash",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[_tweet valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];

        };
        NSLog(@"Pattern 1 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード2
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 2 __block",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
#warning this code is not the way recommended how to use __block ... not recommended
        __block id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];

            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
            
        };
        NSLog(@"Pattern 2 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード3
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"Pattern 3 only declare bt crash",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
#warning this code only crash on Release Build.... Don't use this
        id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
        };
        NSLog(@"Pattern 3 %@",block);
        [dicRow setValue:block forKey:kDicKeyLinkPopBlock];
    }
    { //検証コード4
        NSMutableDictionary * dicRow = [NSMutableDictionary dictionaryWithCapacity:0];
        [dicRow setValue:NSLocalizedString(@"USE THIS:Pattern 4 declare bt and copy block",nil) forKey:kDicKeyLinkPopCellText];
        [arrRows addObject:dicRow];
        id bt = _tweet;
        dispatch_block_t block = ^{
            
            NSString *str = [NSString stringWithFormat:@"%@",[bt valueForKey:@"text"]];
            
            [[UIPasteboard generalPasteboard] setString:str];
            
            [[JTCAppNotificationManager sharedManger] startTimerNotificationWithMessage:NSLocalizedString(@"Copy succeeded", @"Copy succeeded") dulation:2.5 iconName:@"w17-check.png"];
        };
        NSLog(@"Pattern 4 %@",block);
        [dicRow setValue:[block copy] forKey:kDicKeyLinkPopBlock];
    }
    
    NSMutableArray *sections = [NSMutableArray arrayWithObject:arrRows];
    TOVLinkPopoverViewController *controller= [[TOVLinkPopoverViewController alloc] init];
    controller.arrayLink = sections;




デバッグビルドでのログ出力

2012-04-21 13:49:22.109 TOVPopoverSample[10592:707] Pattern 1 <__NSMallocBlock__: 0xfe44ee0>
2012-04-21 13:49:34.734 TOVPopoverSample[10592:707] Pattern 2 <__NSMallocBlock__: 0xfe45b20>
2012-04-21 13:49:39.624 TOVPopoverSample[10592:707] Pattern 3 <__NSMallocBlock__: 0x13dc50>
2012-04-21 13:49:43.224 TOVPopoverSample[10592:707] Pattern 4 <__NSMallocBlock__: 0xfe45110>

見ての通り4パターンともmallocされたブロック、つまりヒープ上に生成されてます。 これではバグの検証は出来ませんね。
別に全部問題ないじゃん、みたいに見えてしまいます。
「ほらスタック領域にブロック生成されるからヒープにコピーしないと」と言う結論を期待したのに「なんで…」と数時間悩んでしまいました。


リリースビルドでのログ出力

2012-04-21 13:10:39.912 TOVPopoverSample[10504:707] Pattern 1 <__NSStackBlock__: 0x2fe54510>
2012-04-21 13:10:54.860 TOVPopoverSample[10504:707] Pattern 2 <__NSMallocBlock__: 0x149250>
2012-04-21 13:11:18.754 TOVPopoverSample[10504:707] Pattern 3 <__NSStackBlock__: 0x2fe544c4>
2012-04-21 13:12:11.226 TOVPopoverSample[10504:707] Pattern 4 <__NSStackBlock__: 0x2fe544ac>

ほぼ予想通りStackBlockとなってます。
このスタックブロックを辞書に登録しておいてスコープ外で使おうとしているのでバグだね、と分かる訳です。
ただパターン2、__block属性の変数をキャプチャしたケースだけはMallocBlockとなってます。
なぜこのケースではヒープに生成されるのか理由は分かりませんが、この為に問題無いかの様に動いたのですね。


結論
Blocksはデバッグビルドでのテストだけで満足してはならない。
リリースビルドでも入念にテストすべきである。


因みに。。。
このケースでは正解は4なのですが、いつもコピーする方が良い訳ではないので注意が必要です。
ヒープにコピーしたらしたで、リークだとか別の心配をしないといけなくなりますから。
4ではブロックをスコープ外で使いたい為にヒープにコピーしています。
ただし!!
ヒープにコピーしたブロックを使い終わったら忘れない様にリリースしないといけません。
今回のはARCなので問題なさそうではありますが。

__blockをキャプチャするとヒープブロックになるのを知ってたのなら2も正解じゃん、
という意見もあるかも知れませんが、そういう裏ワザ的なのはここでは不正解とさせて頂きます(笑)

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;
}

2011年6月10日金曜日

iOS:キーボードをカスタマイズ

元ネタ

カスタムキーボード画面1
顔ボタンを押すとキーボードが変化します。

カスタムキーボード画面2
変化後のカスタムキーボード。ABCボタンで元に戻ります。


このUITextFieldから派生したクラスを画面(*.xib)のカスタムキーボードで入力したいテキストフィールドに設定します。
//
//--------------------------------------------------
//  CustomTextField.h
//--------------------------------------------------

#import 
#import "CustomKeybord.h"


@interface CustomTextField : UITextField {
  @private
  UIView *inputAccessoryView_;
  BOOL useCustomKeybord_;
  NSString *customBtnTxt_;
}

@property (nonatomic, retain) IBOutlet CustomKeybord *keybord;
@property (nonatomic, retain) UIButton *compButton;

- (IBAction)faceBtn1:(id)sender;
- (IBAction)faceBtn2:(id)sender;
- (IBAction)faceBtn3:(id)sender;

@end


//---------------------------------------------------------
//  CustomTextField.m
//---------------------------------------------------------

#import "CustomTextField.h"


@implementation CustomTextField

@synthesize keybord=keybord_;
@synthesize compButton;

- (void)changeKeybord:(id)sender {
  self->useCustomKeybord_ = !self->useCustomKeybord_;
  self->customBtnTxt_ = (self->useCustomKeybord_) ? @"ABC" : @"(^-^)";
  [self reloadInputViews];
}

- (UIView *)inputAccessoryView {
  if (self->inputAccessoryView_ == nil) {
    CGFloat width = [[UIScreen mainScreen] applicationFrame].size.width;
    CGRect accessFrame = CGRectMake(0.0, 0.0, width, 50.0);
    self->inputAccessoryView_ = [[UIView alloc] initWithFrame:accessFrame];
    self->inputAccessoryView_.backgroundColor = [UIColor blueColor];
    self.compButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    self.compButton.frame = CGRectMake(20.0, 5.0, 80.0, 40.0);
    [self.compButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [self.compButton addTarget:self
                   action:@selector(changeKeybord:)
         forControlEvents:UIControlEventTouchUpInside];
    [self->inputAccessoryView_ addSubview:compButton];
    self->customBtnTxt_ = @"(^-^)";
  }
  [self.compButton setTitle: self->customBtnTxt_ forState:UIControlStateNormal];
  return self->inputAccessoryView_;
}

- (UIView *)inputView {
  if (self->useCustomKeybord_) {
    if (self->keybord_ == nil) {
      UINib *customKeybord = [UINib nibWithNibName:@"CustomKeybord" bundle:nil];
      [customKeybord instantiateWithOwner:self options:nil];
    }
    return self->keybord_;
  } else {
    return [super inputView];
  }
}

- (IBAction)faceBtn1:(id)sender {
  self.text = [NSString stringWithFormat:@"%@(^-^)", self.text];
}

- (IBAction)faceBtn2:(id)sender {
  self.text = [NSString stringWithFormat:@"%@(^^;)", self.text];
}

- (IBAction)faceBtn3:(id)sender {
  self.text = [NSString stringWithFormat:@"%@(-.-;)", self.text];
}

@end


カスタムキーボードとしてこのようなNibを作ります。


上のNibに対応するUIViewの派生クラスを作ります。

//
//------------------------
//  CustomKeybord.h
//------------------------

#import 


@interface CustomKeybord : UIView {
}

@end


//-------------------------------
//  CustomKeybord.m
//-------------------------------

#import "CustomKeybord.h"


@implementation CustomKeybord

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

- (void)dealloc
{
    [super dealloc];
}

@end

2011年6月5日日曜日

ブログに数式を貼る


  

このhttp://formula.s21g.com/の後をLaTeXフォーマットで記述すれば下の様になる。



詳しくはここを参照。

2011年5月17日火曜日

MacとWinでキーボード/マウス共用

MacBookとWinPc同時に使いたいけど机の上が狭くて2セットのキーボード/マウスを置けない。
MacBookのキーボード/マウスでWinPCも同時に使いたい、という人にはこのソフト。
Synergy
マウスカーソルを画面の端に移動させるだけでMac→Win,Win→Macと操作が切り替わり、あたかもマルチスクリーンモードの如く一組のキーボード/マウスで2台以上のPCを操作出来ます。
構成は他のパターンでもOKでWinPCのキーボード/マウスでMacを操作したり、もちろんMac同士、Win同士も出来るらしいです。
私はMac→Winの構成しか試してないので、他の構成については他の方のページを参考下さい。

MacとWindowsPCの両方にSynergyをインストールする必要があります。

Macへのインストール

macportを使ってインストールします。
*以下はmacportはインストール済みという前提で進めます。macportのインストール方法は検索すればすぐに分かりますので省きます、悪しからず。
ターミナルで
sudo port install synergy
とすればMacへのインストール完了、これでsynergyをコマンドライン操作で使える様になります。
と…言いたい所ですがGUI慣れした人にはコマンドをぽちぽち入力するのはカッタるいので、
SynergyKMもインストールしましょう。
このインストールでシステム環境設定の中にSynergyKMアイコンが追加されてGUIで操作出来る様になります。

Windows PCへのインストール
ここから

2011年4月30日土曜日

MacにThriftをインストール

Thriftをダウンロード

2011年現在、Thriftはmacportには無いらしい。
なので、
Apache Thriftからダウンロードする。

インストール

ターミナルでダウンロードしたディレクトリに移動する。
インストール先を指定してconfigureを実行する。私の場合、/optの下にしたので、
./configure --prefix=/opt/thrift/
make
とターミナルに打ち込むと普通は問題無いがエラーになった。
ググるとOSX Snow leopard 64 bitな人はそのままではダメらしい。
この記事にしたがって、
export ARCHFLAGS="-arch x86_64"
と打ち込み、もう一度makeしてみる。
make
よし!エラーが無くなった、次に進もう。
sudo make install
OK、これで無事にインストール完了。

2011年4月26日火曜日

iOS:年月日時分を一つのUIDatePickerで入力する

UIDatePickerはプロパティdatePickerModeを変更することで、年月日モードと月日時分(または時分)モードに変更出来る。


それでは年月日時分を一つのUIDatePickerで入力したい場合はどうするのだろうか。
Pickerを二つ用意してそれぞれ年月日用と時分用に分けるのは常套手段かもしれないが、画面を二つに分ける事になり少し面倒臭い。


ユーザーのボタン選択によりdatePickerModeを動的に変えれば良いのではないか。
ボタン押下で下のswitchDateTimeを呼ぶようにする。
//

- (IBAction) switchDateTime {
  switch (datePicker_.datePickerMode) {
    case UIDatePickerModeDate: {
      datePicker_.datePickerMode = UIDatePickerModeDateAndTime;
      [swBtn_ setTitle:NSLocalizedString(@"swichBtn_date", @"") forState:UIControlStateNormal];
    } break;
    case UIDatePickerModeDateAndTime: {
      datePicker_.datePickerMode = UIDatePickerModeDate;
      [swBtn_ setTitle:NSLocalizedString(@"swichBtn_time", @"") forState:UIControlStateNormal];
    } break;
    default:
      assert(0);
      break;
  }
}
うまくいった!!ボタンを押す度にPickerの形が変わる。
と、思いきや駄目だ、モードを切り替えると時分が0時0分に戻ってしまう。
年月日は入力した値が保たれているが、時分だけは幾ら入力してもモードを切り替えると0:00になってしまう。
この様な使い方は想定されていないのか、UIDatePickerは UIDatePickerModeDateでは時分の値を保持しないらしい。


ならばUIDatePickerModeDateに変える直前で日付データを一時退避し後で書き戻す事にする。

//

- (IBAction) switchDateTime {
  switch (datePicker_.datePickerMode) {
    case UIDatePickerModeDate: {
      datePicker_.datePickerMode = UIDatePickerModeDateAndTime;
      [swBtn_ setTitle:NSLocalizedString(@"swichBtn_date", @"") forState:UIControlStateNormal];
    } break;
    case UIDatePickerModeDateAndTime: {
      NSDate *tmp = [datePicker_.date copy]; //<-ここで一時退避
      datePicker_.datePickerMode = UIDatePickerModeDate;
      datePicker_.date = tmp;                          //<-ここで書き戻す
      [tmp release];
      [swBtn_ setTitle:NSLocalizedString(@"swichBtn_time", @"") forState:UIControlStateNormal];
    } break;
    default:
      assert(0);
      break;
  }
}
モードを切り替えても時分は保持されている。
取りあえず問題は解決した様だ。

2011年4月5日火曜日

iOSでBlocks(クロージャ)2

●Objective-CのクラスのインスタンスをBlockから使用する場合に注意する事
思いがけずオブジェクトが自己参照してしまい、そのオブジェクトが解放されなくなるケース。

以下の様なインスタンス変数を持つクラスがあるとする。

//

//--- TestClass.h ---
typedef void (^block_t)();

@interface TestClass : NSObject {
@private
NSNumber* num_;
block_t block_;
}

- (id)initWithNumber:(NSNumber*)num;
- (void)doBlock;

@end

//--- TestClass.m ---
@implementation TestClass

- (id)initWithNumber:(NSNumber*)num {
  self = [super init];
  if (self) {
    num_ = num;
  }
  return self;
}

- (void)dealloc {
  num_ = nil;
  Block_release(block_);
  [super dealloc];
}

- (void)doBlock {
  if (block_) return;

  block_ = Block_copy(^{NSLog(@"memberObj : %@", num_);});
 //ここでselfのretain countが1つ増加、自己参照!!
 //num_のretain countは変化なし

  block_();
}

@end
一見問題の無い様に見えるが、実際は自分自身(self)をretainしている(循環参照している)ので、このクラスのオブジェクトを最後に破棄しようとreleaseしてもdeallocが呼ばれない(リーク!!)

//

TestClass* testObj = [[TestClass alloc] initWithNumber:[NSNumber numberWithInt:10]];
  [testObj doBlock];
  [testObj release]; //ここで破棄、しかしdeallocが呼ばれない
●何故循環参照してしまったのか
ブロック内でnum_を参照しているのでnum_の参照カウンタが増加すると思うだろうがそうではない。
オブジェクトのメソッド内でそのオブジェクトのインスタンス変数を参照するときは、明示的に書かなくてもself->変数、と解釈され、ブロックはselfの参照カウンタを増加させてしまう。

●解決方法
オブジェクトのメソッド内のブロック内でselfを参照しない。
上のケースの様にselfを明示してなくても実はselfを参照するケースに注意する。
上のケースの場合

//

# if 0
  block_ = Block_copy(^{NSLog(@"memberObj : %@", num_);}); //ここを
# else
  NSNumber* tmp = num_;
  block_ = Block_copy(^{NSLog(@"memberObj : %@", tmp);});   //こう書き換える
# endif
一旦ローカル変数へコピーする事でブロック内でself->と参照される事を防ぐ。
これでブロックはselfを参照せずにnum_だけを参照する様になる。

●雑感
クラスのメソッド内でBlocksを使うときは毎回こういう冗長なことをしないといけないのだろうか。
一見意図の見えづらいコードだし:(

2011年4月4日月曜日

iOSでBlocks(クロージャ)1

iOS 4プログラミングブックはBlocksについて分かりやすく書いてある。

●Blocksとは
いわゆるクロージャ。
関数内で宣言できるいわゆる無名関数だが、ただの無名関数ではない。
引数以外に外側のスコープのローカル変数を参照出来る。

前に^が付いているのがBlock構文である。
戻り値がvoidなので省略が可能で
^(int arg) { /* Block処理 */ };
としたが、省略しない場合は
void ^(int arg) { /* Block処理 */ };
となる。
ちなみに引数もvoidなら更に省略できて
^{ /* Block処理 */ };
と記述出来る。

下のコードでは変数fnにBlockを代入し、aとbをインクリメントした後でfnを関数ポインタの様に呼び出している。

//
int a = 1, b = 1;
void (^fn)(int) = ^(int arg) {printf("a=%d, b=%d\n", arg, b);};//(1)
++a; ++b;
fn(a);  //a=2, b=1 と出力される
//
ローカル変数aは引数として渡される直前でインクリメントされているのでa=2と表示される。
一方bはb=1と表示される。
aと同じ行でインクリメントされているのでfn(a)呼び出し時点でbも値は2である。

では何故b=1表示されるのだろうか。
これがクロージャたる所以で(1)でfnにBlockが代入された時点で変数bをキャプチャ(コピー)されるからである。

●キャプチャされたローカル変数はどこに記憶されるのか?
ローカル変数を使っていない場合:.dataセクション
ローカル変数を使っている場合:スタック
上の例はローカル変数をキャプチャするので、この場合ブロックはスタック上に配置される。
このスタック上に配置されるブロックがうっかりすると厄介な間違いの元になる。
★間違った使い方の例

//
typedef void (^block_t)();
  block_t blocks[5];
  int i;
  for(i=0; i < sizeof(blocks)/sizeof(blocks[0]); ++i) //Loop1
    blocks[i] = ^{printf("%d\n", i);};
  for(i=0; i < sizeof(blocks)/sizeof(blocks[0]); ++i) //Loop2
    blocks[i]();

実行結果
4
4
4
4
4

意図した実行結果は以下だったのだが...
0
1
2
3
4

何が間違っているのか?

Loop1のブロック^{printf("%d\n", i);};はループ毎に一時的にスタック上に配置され、そのスタック位置のアドレスは各ループiのblocks[i]に保存される。 ループが進むたびに破棄されスタック位置は元に戻る。 つまりループ毎にブロックはスコープから外れるので、このケースではループの全ての回で同じスタック位置に一時ブロックが上書きされる事になる。 blocks[0]からblocks[4]には結果的に同じアドレスを指しているので実行結果は全て4になるのだが、Loop1を抜けた時にはこのアドレスはダングリングなのでLoop2を実行する事自体が間違っている。

●この問題の回避方法
Block_copyを使ってスタックに一時的に生成されたブロックをヒープにコピーする。

//
typedef void (^block_t)();
  block_t blocks[5];
  int i;
  for(i=0; i < sizeof(blocks)/sizeof(blocks[0]); ++i)
    blocks[i] = Block_copy(^{printf("%d\n", i);});//Blockをヒープにコピー
  for(i=0; i < sizeof(blocks)/sizeof(blocks[0]); ++i){
    blocks[i]();
    Block_release(blocks[i]);//ヒープ上のものは使い終わったらreleaseする
  }

実行結果
0
1
2
3
4

2010年5月28日金曜日

Mac:EclipseでYAMLを使う

YAMLのライブラリにJYamlを使ってみる。

ライブラリの配置

http://sourceforge.net/projects/jyaml/ 
からjyaml-1.3.jarをダウンロードし適当なディレクトリ、例えばホーム下のディレクトリ$HOME/YAMLにjyaml-1.3.jarを置く。

ビルド・パスへ追加


Eclipseのメニュー Project → Properties
Add External JARs...から先程のjyaml-1.3.jarを選択する。
















インポート

使いたいソースファイルに
import org.ho.yaml.Yaml;
を追加。
後はこのへんを参考に

http://gihyo.jp/dev/serial/01/yaml_library/0006?page=2