Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Review Boardを使ってみようと

公式サイトInstalling on Linuxを参考に、手近なApache 2.0サーバにReview Board 1.0 beta 2を入れてみた。
500エラーが返ってきた。
エラーログを確認すると

Client does not support authentication protocol requested by server; consider upgrading MySQL client

とある。
「古いMySQLクライアントで新しい認証プロトコルを要求するMySQLサーバに接続しようとしている」らしい。
このサーバには、もともと「Apache 2.0」「MySQL 3」「Python 2.3」がセットで入っているのだが、それとは別に「MySQL 5」「Python 2.6」もインストールしていて、MySQLサーバは5のもののみが動作している。
Review BoardはPython 2.6にインストールしており、Python 2.6はMySQL 5のクライアントを使うように設定してある。
実際、Python 2.6のインタプリタで

>>> import MySQLdb
>>> MySQLdb._mysql.get_client_info()

とした場合はちゃんと5系の値を返す。
そして、Apache(+mod_python)ではなくrunserverで動かした場合、少なくともデータベース関連ではエラーを出さずに動作する。
にもかかわらず、Apacheで動かした場合は上記のエラーが出て動かない。
試しにmod_python上で MySQLdb._mysql.get_client_info() をしてみたところ、案の定というかなんというか、3系の値を返されてしまった。
しばらく試行錯誤してみたものの、どうしても解決策が見つからなかったので、諦めて「SQLite」を使うことにした。
SQLiteはこのサーバには入っていないので、「SQLite3をインストール」→「sqlite3をimportするためにPythonを再インストール」し、実行。
今度は500エラーはでない。が――Webページも出てこない(延々レスポンス待ち)。
流れを見るに、どうもDBに接続するところで止まってしまっているらしい。
らしいが……よくわからん。
これもまた、runserverでは正常に動作する。
もしかしたらmod_pythonが悪いのか?とmod_wsgiに変えてみたが、結果は両データベースともmod_pythonの時と同じ。
ならばもうApacheを変えてみるしかないと、「Apache 2.2」を別途入れ、mod_wsgiで動かしてみた。

動いた!\(^o^)/
しかもMySQLでもSQLiteでもどちらでも動作する。
何かあのApacheに強力な呪縛でもあったのか……

まあいい。とにかく動くようになったんだし、試しに使ってみよう。
リポジトリやらグループやらを設定し、TortoiseSVNで適当にパッチを作って「Create Review Request」。
……何も起こらない。
いや、ボタンは反応しているのだが、レビューが追加されない。
「なんだまたApache関連の問題か?('A`)
などと思ってしまったが、調べたらこれは普通にReview Boardの問題だった。おのれ。
とりあえずinfo['origInfo']やinfo['newInfo']に「リビジョン」や「作業コピー」という文字列が含まれていたら、それを元の英単語にreplaceするようにしておけば動く(これらの文字列がローカライズされていることを想定していないため、UnicodeDecodeErrorが食われてレビューの追加とページの遷移に失敗している)ようなので、今のところはそれですませておこう……

橋向かいのsetObject:forKey:

タグ: Objective-C

NSDictionaryのtoll-free bridgeについてコメントしていて、ふと浮かんだ疑問。
NSMutableDictionaryのsetObject:forKey:は「キーをコピーして」格納する。
CFMutableDictionaryのCFDictionarySetValueは「キーをCFDictionaryRetainCallBackに掛けて」格納する。
では、NSMutableDictionary *をtoll-free bridgeでCFMutableDictionaryRefに型変換してCFDictionarySetValueした場合、はたしてキーはコピーされるか否か。

「setObject:forKey:が自分でキーをコピーしてCFDictionarySetValueに渡す」ような流れになっているのであれば、コピーはされないはず。
しかし「CFDictionaryRetainCallBackがキーをコピーする」ようになっているとしたら、コピーされることになる。

@interface Hoge : NSObject<NSCopying>
{
    NSInteger value;
}
- (void)log;
@property NSInteger value;
@end

@implementation Hoge
@synthesize value;
- (id)copyWithZone:(NSZone *)zone
{
    Hoge *hoge = [[Hoge allocWithZone:zone] init];
    hoge.value = self.value;
    return hoge;
}
- (void)log
{
    NSLog(@"%d: %p (retainCount: %d)", self.value, self, [self retainCount]);
}
Hoge *hoge1 = [[[Hoge alloc] init] autorelease];
hoge1.value = 1;
[hoge1 log];

Hoge *hoge2 = [[[Hoge alloc] init] autorelease];
hoge2.value = 2;
[hoge2 log];

NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:hoge1 forKey:hoge1];
CFDictionarySetValue((CFMutableDictionaryRef)dict, hoge2, hoge2);
for(Hoge* hoge in [dict keyEnumerator])
{
    [hoge log];
}

MacOS X 10.5.6での結果。

1: 0x103a20 (retainCount: 1)
2: 0x1052b0 (retainCount: 1)
1: 0x106160 (retainCount: 1)
2: 0x1052b0 (retainCount: 3)

CFDictionarySetValueで追加した方(2:)はアドレスが変わっていない(=コピーしていない)。
参照数も順当な値(「hoge2」「キー」「値」の計「3」)を示している。
つまりこの方法を使えば、「NSCopyingプロトコルに準拠しないオブジェクト」をNSDictionaryのキーにすることも一応可能というわけだ。
よく考えたら、「CFMutableDictionaryRefをNSMutableDictionary *に変換してsetObject:forKey:する場合もキーはコピーされなければならない」のだから、CFDictionaryRetainCallBackではなく、setObject:forKey:がキーをコピーするのは当然と言える。

タイルビューの挙動確認用サンプル

タグ: iPhone Objective-C Quartz

layerClassがCATiledLayerなUIView継承クラスの挙動確認を目的に作成したクラス。
タイルごとにそのタイルの担当範囲やら拡大率やらを描画する。

@interface TiledView : UIView<UIScrollViewDelegate>
@property (nonatomic, readonly) CATiledLayer *tiledLayer;
@end

@implementation TiledView
/// @name TiledView
//@{
- (CATiledLayer *)tiledLayer
{
    return (CATiledLayer *)self.layer;
}
//@}
/// @name UIView オーバーライド
//@{
+ (Class)layerClass
{
    return [CATiledLayer class];
}
- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGAffineTransform t = CGContextGetCTM(context);
    
    int xmin = CGRectGetMinX(rect);
    int ymin = CGRectGetMinY(rect);
    int xmax = CGRectGetMaxX(rect);
    int ymax = CGRectGetMaxY(rect);
    int x = xmin / CGRectGetWidth(rect);
    int y = ymin / CGRectGetHeight(rect);
    NSString *string =
        [NSString stringWithFormat:
            @"%gx [%d,%d]\n(%d,%d)-(%d,%d)",
            t.a, x, y, xmin, ymin, xmax, ymax
        ];
    [[UIColor whiteColor] set];
    [string drawInRect:rect withFont:[UIFont systemFontOfSize:(20 / t.a)]];
}
//@}
/// @name UIScrollViewDelegate 実装
//@{
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
{
    [self setNeedsDisplay];
    return NO;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self;
}
//@}
@end

このようにUIScrollViewのサブビュー兼デリゲートオブジェクトとして使う。

- (void)applicationDidFinishLaunching:(UIApplication *)application
{ 
    TiledView* tiledView = [[TiledView alloc] initWithFrame:CGRectMake(0, 0, 4096, 4096)];
    tiledView.tiledLayer.levelsOfDetail = 5; // 全「5」階層
    tiledView.tiledLayer.levelsOfDetailBias = 3; // 拡大方向へは「2x」「4x」「8x」の「3」階層
     
    UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:window.frame];
    scrollView.contentSize = tiledView.bounds.size;
    scrollView.maximumZoomScale = 16;
    scrollView.minimumZoomScale = 0.25;
    scrollView.delegate = tiledView;
    [scrollView addSubview:tiledView];
    [tiledView release];
    
    [window addSubview:scrollView];
    [scrollView release];
    
    [window makeKeyAndVisible];
}

いろいろ設定をいじりながら使ってみたところ、iPhone実機(2.2.1)では綺麗に表示されるのに、シミュレータでは大きい倍率で描画が乱れることがある。

tiled_layerrrr.png

なぜか描画されないタイルができることも。

blank_tile.png

また実機・シミュレータともに、frame.sizeとlevelsOfDetailBiasがかなり大きいTiledViewに対してsetNeedsDisplayを送る(ステータスバーをタップする)とSpringBoardプロセスがしばらく処理を占有してしまい、操作を一切受け付けなくなる。
こうなると処理が終わるまでSpringBoardに戻ることもできなくなるので非常に鬱陶しい。

concurrent operationがstartするスレッド

タグ: Objective-C NSOperation

isConcurrentではないNSOperationのmainは新しく生成されたスレッドから呼ばれる。
では逆に、isConcurrentなNSOperationのstartはどのスレッドから呼ばれるのか。
ここでは、あまりこのような使い方をすることはないかもしれないが、「isConcurrentではないNSOperation」と「isConcurrentなNSOperation」を混ぜて使い、それらの各種メソッドが呼ばれるスレッドを調べてみる。

#pragma mark base class

@interface OperationBase : NSOperation
{
    NSUInteger number;
}
- (id)initWithNumber:(NSUInteger)n;
- (void)log:(NSString*)string;
@end

@implementation OperationBase
- (id)initWithNumber:(NSUInteger)n
{
    if(self = [super init])
    {
        number = n;
    }
    return self;
}
- (void)log:(NSString*)string
{
    NSLog(@"%@ (%d)%@", [NSThread currentThread], number, string);
}
@end

#pragma mark Concurrent operation class

@interface ConcurrentOperation : OperationBase
{
    BOOL isExecuting;
    BOOL isFinished;
}
@property BOOL isExecuting;
@property BOOL isFinished;
@end

@implementation ConcurrentOperation
@synthesize isExecuting, isFinished;
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key
{
    if([key isEqualToString:@"isExecuting"] || [key isEqualToString:@"isFinished"])
    {
        return YES;
    }
    return [super automaticallyNotifiesObserversForKey:key];
}
- (BOOL)isConcurrent
{
    return YES;
}
- (void)start
{
    [self log:@"Concurrent start"];
    self.isExecuting = YES;
    [NSThread detachNewThreadSelector:@selector(thread) toTarget:self withObject:nil];
}
- (void)thread
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    [self log:@"Concurrent thread ->"];
    [NSThread sleepForTimeInterval:0.1];
    self.isExecuting = NO;
    self.isFinished = YES;
    [pool release];
    [self log:@"Concurrent thread <-"];
    [NSThread exit];
}
@end

#pragma mark Non-concurrent operation class

@interface NoncurrentOperation : OperationBase
@end

@implementation NoncurrentOperation
- (void)start
{
    [self log:@"Noncurrent ->"];
    [super start];
    [self log:@"Noncurrent <-"];
}
- (void)main
{
    [self log:@"Noncurrent main"];
    [NSThread sleepForTimeInterval:0.1];
}
@end

#pragma mark -

void run()
{
    NSOperationQueue* queue = [[NSOperationQueue alloc] init];
    [queue setMaxConcurrentOperationCount:1];
    NSLog(@"%@ main", [NSThread currentThread]);
    [queue addOperation:[[[ConcurrentOperation alloc] initWithNumber:1] autorelease]];
    [queue addOperation:[[[NoncurrentOperation alloc] initWithNumber:2] autorelease]];
    [queue addOperation:[[[ConcurrentOperation alloc] initWithNumber:3] autorelease]];
    [queue addOperation:[[[ConcurrentOperation alloc] initWithNumber:4] autorelease]];
    [queue addOperation:[[[NoncurrentOperation alloc] initWithNumber:5] autorelease]];
    [queue addOperation:[[[ConcurrentOperation alloc] initWithNumber:6] autorelease]];
    [queue waitUntilAllOperationsAreFinished];
    [queue release];
}

現在の環境(Mac OS X 10.5.6、XCode 3.1.2)での実行結果は以下の通り。

<NSThread: 0x103b00>{name = (null), num = 1} main
<NSThread: 0x103b00>{name = (null), num = 1} (1)Concurrent start
<NSThread: 0x10e710>{name = (null), num = 2} (1)Concurrent thread ->
<NSThread: 0x10e710>{name = (null), num = 2} (1)Concurrent thread <-
<NSThread: 0x110090>{name = (null), num = 3} (2)Noncurrent ->
<NSThread: 0x110090>{name = (null), num = 3} (2)Noncurrent main
<NSThread: 0x110090>{name = (null), num = 3} (3)Concurrent start
<NSThread: 0x110090>{name = (null), num = 3} (2)Noncurrent <-
<NSThread: 0x10f570>{name = (null), num = 4} (3)Concurrent thread ->
<NSThread: 0x10f570>{name = (null), num = 4} (4)Concurrent start
<NSThread: 0x10f570>{name = (null), num = 4} (3)Concurrent thread <-
<NSThread: 0x10f5b0>{name = (null), num = 5} (4)Concurrent thread ->
<NSThread: 0x10f5b0>{name = (null), num = 5} (4)Concurrent thread <-
<NSThread: 0x10eb70>{name = (null), num = 6} (5)Noncurrent ->
<NSThread: 0x10eb70>{name = (null), num = 6} (5)Noncurrent main
<NSThread: 0x10eb70>{name = (null), num = 6} (6)Concurrent start
<NSThread: 0x10eb70>{name = (null), num = 6} (5)Noncurrent <-
<NSThread: 0x10f000>{name = (null), num = 7} (6)Concurrent thread ->

addOperationした時点で処理を開始できる(同時処理数に空きがある)場合は別として、基本的にはisFinishedをYESに設定した時、そのキー値監視の流れの中で次のNSOperationのstartが呼ばれている。
次のNSOperationがisConcurrentの場合、startはisFinishedをYESにしたスレッドからそのまま呼ばれる。
そうではない場合、startは(detachNewThreadSelectorのように)新しく生成されたスレッドから呼ばれる。

Xcodeでコードカバレッジ

タグ: Xcode gcc iPhone

Xcodeのビルド設定をボーっと眺めていたら、ふと「Generate Test Coverage Files」という項目に目がいった。

Coverage……コードカバレッジか。
そういえばXcodeでコードカバレッジを求めたことはまだない。
VS2005TEfSDでは、付属の「vsinstr」「vsperfmon」「vsperfcmd」を駆使してそこそこ見栄えの良い情報を得ることができたが、Xcodeではどうなのだろう。
ざっと調べたところでは、Google codeのcoverstoryを使うのが手軽で良さげだ。
特に使い方に悩むものでもないが、一応手順をメモしておく。

  1. QA1514を参考に、カバレッジ用の構成を作成する(Figure 1の上まで)
  2. カバレッジ用の構成をアクティブにして、UsingCoverstoryにあるAppleScriptを実行する(QA1514のFigure 1から下に相当する設定が行われる)
  3. ターゲット(アプリケーションなりユニットテストなり)をビルドする
  4. ターゲットを起動し、カバレッジを求めたい操作を行ったら終了させる
  5. 「構成ごとの中間ビルドファイルのパス」をcoverstoryで開く

この設定はiPhone Simulator向けのビルドでも有効。
例えば「Window-based Application」を作って特に何も手を加えずビルド・実行・終了してみると

nerver_dealloc.png

このように、アプリケーションデリゲートの「applicationDidFinishLaunching:」は1回だけ呼ばれ、「dealloc」は1度も呼ばれなかったことが分かる。

04月04日のココロ日記(BlogPet)

むいさんは絶賛が宝物なのです!

*このエントリは、ブログペットのココロが書いてます♪

チェンジ! ボラタイル!

タグ: Objective-C C++ gcc

以下のObjective-C++コードをgcc 4.2.1でコンパイルすると、foo2の初期化呼び出しでコンパイルエラーが発生する。

@interface Foo : NSObject
- (id)initWithReference:(int&)n;
@end

void bar(int&);

void baz()
{
    int n = 1;

    // 問題なし
    Foo* foo1 = [[Foo alloc] initWithReference:n];
    // 問題なし
    bar(n);
    
    @synchronized(foo1) {}
    
    // error: invalid initialization of reference of type 'int&' from expression of type 'volatile int'
    Foo* foo2 = [[Foo alloc] initWithReference:n];
    // 問題なし
    bar(n);
}

GCCのBugzillaには、これに似た次のようなバグが上げられている。

Bug 35967 - Local variables interpreted as volatile following a @try block - GCC Bugzilla

「ローカル変数の型修飾がブロック途中で変わる」とは……考えになかった。
しかし、setjmp/longjmpを使う@try/@catch/@finallyはともかく、@synchronizedではローカル変数をvolatile修飾する必要などないのでは。

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。