0

実行中のブロックへの参照を保持する変数の割り当て解除が原因でクラッシュしました。コード例は次のとおりです。

これは、リリースで現在間違っていることです。同じデバイスでのデバッグでは正常に実行されます。クラッシュするには、アドホックとして実行する必要があります。

- (void)test {
    _test = [self doLater:^{
      _count++;
       [self test];
   } :3];
}

これは NSObject カテゴリで定義されています。

- (DoLaterProcess *)doLater:(void (^)())method :(double)delay {
    return [[DoLaterProcess new] from:method :delay];
}

使用されたクラスの実装を終了します:

@implementation DoLaterProcess {
    id _method;
    BOOL _stop;
}

- (void)methodToPerform:(void (^)())methodToInvoke {
    if (_stop)return;
    if (NSThread.isMainThread) methodToInvoke();
    else [self performSelectorOnMainThread:@selector(methodToPerform:)      withObject:methodToInvoke waitUntilDone:NO];
}

- (DoLaterProcess *)from:(void (^)())method:(NSTimeInterval)delay {
    [self performSelector:@selector(methodToPerform:) withObject:method afterDelay:delay];
    _method = method;
    return self;
}

- (void)stop {
    _stop = YES;
    [NSObject cancelPreviousPerformRequestsWithTarget:self     selector:@selector(methodToPerform:) object:_method];
}

@end

したがって、_test 変数の割り当てが解除され、その中で割り当てが解除されている間におそらくブロックされることを理解していますか? それがクラッシュする理由ですか?しかし、デバッグでクラッシュしないのはなぜですか?デバッグでもコンパイラを強制的にクラッシュさせることはできますか? ありがとうございました。

4

1 に答える 1

1

ブロックはローカル状態をキャプチャします。あなたの場合、ブロックは と をキャプチャ_countしていselfます。これを効率的に行うために、ブロックを作成すると、最初はスタックに存在し、そのメソッドが返されない限り安全に使用できるという効果があります。したがって、ブロックを下に渡すことはできますが、ブロックを保持したり、ヒープに移動せずに上に渡したりすることはできません。copyそれらを ing することでそれを実現します (また、ブロックが既にヒープ上にあるかのようcopyに動作するように定義されてretainいるため、過剰コピーの費用はかかりません)。

あなたの場合、正しいことはdoLater::ブロックをコピーすることです(ただし、記録のために、名前のないパラメーターは非常に貧弱な方法と見なされます)。

メソッドをインスタンス変数に割り当て、後で渡すようにスケジュールする理由について少し混乱していますが、最も簡単な修正は次のとおりです。

- (DoLaterProcess *)from:(void (^)())method:(NSTimeInterval)delay {
    method = [method copy];
    [self performSelector:@selector(methodToPerform:) withObject:method afterDelay:delay];
    _method = method;
    return self;
}

これが 4.6 で壊れたように見える理由について: 文書化されていない動作に依存していたため (文書化されていない動作が自然であるように感じられますが)、ツールセットまたは OS の変更 (または実際にはまったく変更なし) がそれに影響を与えることが許可されています。 .

(余談ですが、GCDに組み込まれているものの多くを再実装しているようです。どちらの場合も、キューとして提供from::するdispatch_aftermethodToPerform:を直接置き換えることができます)。dispatch_asyncdispatch_get_main_queue()

于 2013-02-05T21:10:00.447 に答える