8

Apple LLVMコンパイラ3.0を使用し、-O3でコンパイルすると、NSCoderで異常なクラッシュが発生することがわかりました。デバイスでのみクラッシュします。iOS5を実行しているiPhone4、iOS5を実行しているiPad2、およびiOS4を実行しているiPad1をテストしました。すべて同じようにクラッシュします。コードの関連セクションは次のとおりです。

-(id)initWithCoder:(NSCoder*)decoder
{
    if (![super init])
    {
        return nil;
    }

    NSUInteger length = 0;

    uint8_t* data = (uint8_t*)[decoder decodeBytesForKey:BBKey returnedLength:&length];

    m_value = *(BBPointI32*)data;

    return self;
}

そして、BBPointI32とは次のとおりです。

typedef struct
{
    NSInteger x;
    NSInteger y;
}
BBPointI32;

が逆参照されたEXC_BAD_ACCESSときに発生します。dataこれはnullポインタの問題ではありません。GDBをアタッチすると、長さが8で、sizeof(BBPointI)も8であり、データが正しいことがわかります。

分解を見ると、クラッシュは次のように発生しています。

ldrd    r2, r3, [r0]

これは問題ないようです。r0には、のアドレスである0xb546eが含まれていますdata。そのメモリを調べると、期待するデータが含まれていることがわかります。興味のある人は、r2に72(それが何であるかはわかりません)が含まれ、r3には8(おそらくの値length)が含まれます。

誰かがこの問題に光を当てることができますか?

4

4 に答える 4

10

ldrd では、アドレスを 8 バイトでアラインする必要があります。*(BBPointI32 *)data イディオムは、データが 8 バイトでアラインされていないため安全ではありません。代わりに memcpy を使用してバイトを構造体に取得します。

于 2011-10-11T17:10:27.263 に答える
4

ARCを使用していますか?もしそうなら、問題はコンパイラが呼び出しdecoder後に自由に解放できることだと思いdecodeBytesForKey:ます(したがって、戻り値が指すバッファを解放します)。

これは、ガベージ コレクションが持っている内部ポインターの問題と同じです。CFRetain/CFReleaseデコーダーの寿命を延ばすか[decoder self]、メソッドに後で追加して、その時点まで存続させることができます。

で注釈decoderを付けることでこの問題を解決できるのではないかと思いますが、その属性__attribute__((objc_precise_lifetime))についての私の理解はやや曖昧です。

于 2011-10-11T16:44:36.813 に答える
3

あなたの例では、潜在的なヘルパーが疑問視する多くの変数が残されています。例: この unarchiver にファンキーなものがあるとしたら? メモリは正しく管理されていますか?

あなたが見ているクラッシュを再現することができました.-O3が有効になっている場合にのみ発生し、最適化のためにNoneが選択された場合では発生しないことを確認できます. これは、コーダーのメモリ管理などの外部変数を排除するクラッシュ コードの削減です。以下のコードは、意図的にすべてのオブジェクトを保持して、クラッシュが偶発的なオーバーリリースまたはサイド リリースに関連している可能性を排除することに注意してください。アンディが別の回答で示唆したように、ARCを使用する効果:

typedef struct
{
    NSInteger x;
    NSInteger y;
}
BBPointI32;

- (void) testDecoding
{
    NSString* myKey = @"Testing";

    // First get an coder with bytes in it
    NSMutableData* myData = [[NSMutableData data] retain];
    NSKeyedArchiver* myCoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:myData];

    BBPointI32 encodedStruct = {1234, 5678};
    [myCoder encodeBytes:(const uint8_t *)&encodedStruct length:sizeof(encodedStruct) forKey:myKey];
    [myCoder finishEncoding];

    // Now decode it
    BBPointI32 decodedStruct;
    NSUInteger decodedLength = 0;
    NSKeyedUnarchiver* myDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:myData];
    uint8_t* data = (uint8_t*)[myDecoder decodeBytesForKey:myKey returnedLength:&decodedLength];
    decodedStruct = *(BBPointI32*)data;
    NSLog(@"Got decoded struct with x = %ld, y = %ld, length = %lu", decodedStruct.x, decodedStruct.y, decodedLength);
}

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    NSLog(@"Testing decoding");
    [self testDecoding];
}

これは問題のより簡潔な説明を与えてくれると思います。助けたい人は誰でも飛び込むための基礎として使用できます.これまでの私の推測では、これは LLVM 3.0 の最適化バグですが、他の誰かがより良い理論を持っているかもしれません.何が起こっている。

質問では言及されていませんが、デバイスのクラッシュで気付いた点は、不良アクセス例外の理由として EXC_ARM_DA_ALIGN エラーの言及が失敗に伴うことです。同じ症状をほのめかし、おそらくクラッシュの原因と思われるブログ投稿を Google で検索しました。

http://www.galloway.me.uk/2010/10/arm-hacking-exc_arm_da_align-exception/

実際、上記の行を変更することにより

decodedStruct = *(BBPointI32*)data;

memcpy(&decodedStruct, data, sizeof(decodedStruct));

クラッシュ動作は軽減されたようで、コードは期待どおりに動作します。

于 2011-10-11T17:01:21.943 に答える
1

「EXC_ARM_DA_ALIGN」と「EXC_BAD_ACCESS」をグーグルで検索して、このスレッドにアクセスしました。このエラーは比較的単純なものが原因で発生したため、他の回答はどれも役に立ちませんでした。私は書いた:

theArray = [[NSArray alloc] initWithObjects:@"first", @"second", @"third",
                        @"fourth", @"fifth", "sixth", nil];

つまり、文字列リテラルの前で@を省略していました。それを元に戻すと、エラーが解決しました。

于 2012-11-21T21:14:53.447 に答える