私が書いた列挙子で奇妙なバグに出くわしました:
// typedef for reference
typedef void (^OGWEntityEnumerationBlock)(OGWEntity* entity, BOOL* stop);
-(void) enumerateEntitiesInCategory:(const OGWEntityCategory*)category
usingBlock:(OGWEntityEnumerationBlock)block
{
BOOL stop;
NSArray* entitiesInCategory = [_cagetorizedEntities objectForKey:category];
for (OGWEntity* entity in [entitiesInCategory reverseObjectEnumerator])
{
block(entity, &stop);
if (stop)
{
break;
}
}
}
の最初の使用は正常にenumerateEntitiesInCategory:usingBlock:
機能します。最終的に呼び出し元は、探していたエンティティを見つけ、*stop
パラメーターを に設定しますYES
。
の次の使用はenumerateEntitiesInCategory:usingBlock:
、最初の反復の直後に終了します。詳細に調べると、前の反復でパラメーターが YESに設定されるとすぐにstop
初期化されるようになりました。これを修正するには、ストップ変数を意図的に初期化する必要があります。YES
*stop
NO
どうすればいいの?stop 変数のアドレス (おそらく偶然) は、「古い」値がそこにあることを説明する呼び出し全体で同じままである可能性があることを知っています。しかし、ARC は初期化されていない型がそれぞれ 0 に設定されていることを保証するという印象を受けていたnil
ので、関数が呼び出されるたびに BOOL を NO に設定すべきではありませんか?
興味深いことに、__block BOOL stop;
stop 変数を使用すると、デバッガーによると、初回であっても常にYES に初期化されます (これも、初期化されていない値がそのアドレスにあった場合は、おそらく偶然です)。
これは、初期化されていない変数がゼロであり、ARC であると誤って想定している可能性があることを示しているようです。おそらく、ARC はオブジェクト型を nil で初期化することだけを気にし、整数型は気にしませんか? それとも、これは ivar にのみ当てはまり、ローカル変数には当てはまりませんか?