トップ «前の日記(2013-11-05) 最新 次の日記(2013-11-15)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2013-11-14

_ iOSでdispatch_after

以下のインターフェイスを持つオブジェクトを作った。

@interface foo
-(void) doSomething;
-(void) abort;
@end

doSomethingの中で、後でやる処理が必要となったのでdispach_afterを使った。そこから呼び出し元へ結果を通知する。abortは、その動作を取り消すためのメソッドだ。

最初はあまり考えていなかったので、次のようにした。

@implements Foo
BOOL _abortRequest;
-(void) doSomething
{
    _abortRequest = NO;
    ...
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC); // 1秒後に実行
    dispatch_after(t, dispatch_get_main_queue(), ^(void) {
        if (!_abortRequest) {
            ...
            NSNotification* n = [NSNotificcation notificationWithName:@"fooResult" object:nil];
            [[NSNotificationCenter defaultCenter] postNotification:n];
        }
    }
}
-(void)abort
{
    _abortRequest = YES;
}

ところが、呼び出して中断するテストコードを書くとうまく行かない。後続のテストに介入される。

NSUInteger _calledCount;
-(void)testA
{
    Foo* foo = [[Foo alloc]init];
    [foo doSomething];
    [foo abort];
}
-(void)testB
{
    _calledCount = 0;
    Foo* foo = [[Foo alloc]init];
    [foo doSomething];
    for {
        [[NSRunLoop currentRunLoop] runUnitlDate:.....// 時々状態を眺める
    };
    STAssertEqeuals(_calledCount, 1, @"bad called count:%d", _calledCount); // 2は1ではないになる。
}
-(void)notificationCallback:(NSNotification*)notification
{
    _calledCount++;
}

ということは、dispatch_afterのブロック内で参照しているselfが元のselfではないのだということに気づくまでに時間がかかったが(あるいは、インスタンス変数がコピーされているのかも知れない)、そういうことだろう。.NET Frameworkのデリゲートが参照するthisは呼び出し時のthisだと思うのだが、実際のところどうなんだっけ?

いずれにしても、selfが元のselfでないなら、元のselfを経由してインスタンス変数を参照するしかない。

@interface Foo()
@property BOOL abortRequest;
@end
@implements Foo
@synthesize abortRequest = _abortRequest;
-(void) doSomething
{
    _abortRequest = NO;
    ...
    dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC); // 1秒後に実行
    __block Foo* __weak wself = self; // ブロック内で参照するために呼び出し時のselfを取り出す。
    dispatch_after(t, dispatch_get_main_queue(), ^(void) {
        if (wself && !wself.abortRequest) { // デアロケートされるとwselfはnilになるから、こうする必要がありそうだ(追記)
            ...
            NSNotification* n = [NSNotificcation notificationWithName:@"fooResult" object:nil];
            [[NSNotificationCenter defaultCenter] postNotification:n];
        }
    }
}

で、解決。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|

ジェズイットを見習え