2011-10-11 21 views
8

Tôi đã tìm thấy một crasher bất thường với NSCoder khi sử dụng Apple LLVM Compiler 3.0 và được biên dịch với -O3. Nó chỉ treo trên các thiết bị. Tôi đã thử nghiệm iPhone 4 chạy iOS 5, iPad 2 chạy iOS 5 và iPad 1 chạy iOS 4. Tất cả sự cố đều giống nhau. Dưới đây là phần có liên quan của mã:Tai nạn trên thiết bị iOS khi dereferencing một con trỏ trở về bởi decodeBytesForKey của NSCoderer

-(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; 
} 

Và đây là những gì một BBPointI32 là:

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

Các EXC_BAD_ACCESS xảy ra khi data được dereferenced. Đây là không phải vấn đề về con trỏ null. Nếu tôi đính kèm GDB, tôi có thể thấy rằng chiều dài là 8, sizeof (BBPointI) cũng là 8 và dữ liệu là chính xác.

Nếu tôi nhìn vào tháo gỡ, các vụ tai nạn xảy ra vào lúc:

ldrd r2, r3, [r0] 

Mà có vẻ tốt đẹp. r0 chứa 0xb546e, là địa chỉ của data. Khi tôi kiểm tra bộ nhớ đó, tôi có thể thấy rằng nó chứa dữ liệu tôi mong đợi. Đối với bất kỳ ai quan tâm, r2 chứa 72 (không chắc chắn đó là gì) và r3 chứa 8 (hầu hết có thể là giá trị của length).

Có ai có thể làm sáng tỏ vấn đề này không?

Trả lời

10

ldrd cần địa chỉ được căn chỉnh 8 byte. Thành ngữ dữ liệu * (BBPointI32 *) không an toàn vì dữ liệu không được căn chỉnh 8 byte. Sử dụng memcpy để có được các byte vào struct thay vào đó.

+0

Cảm ơn, đó là điểm trên. Vấn đề * là * liên quan đến quyết định của trình biên dịch để sử dụng 'ldrd'. Khi tôi truyền 'dữ liệu' sang' BBPointI32 * ', nó giả định rằng điều này có nghĩa là con trỏ đã được căn chỉnh đúng cách, nhưng không phải. Vì vậy, thay vì: m_value = * (BBPointI32 *) dữ liệu; Tôi cần sử dụng: memcpy (& m_value, dữ liệu, độ dài); –

+0

@biorhythmist .. không chỉ là một twitterer vui nhộn :) – ohhorob

+0

Tôi đang trải qua một cái gì đó rất giống nhau. Xảy ra trên iPad 3 và _not_ trên iPad 2! –

4

Bạn đang sử dụng ARC? Nếu vậy, tôi tin rằng vấn đề là trình biên dịch là miễn phí để phát hành decoder sau khi cuộc gọi decodeBytesForKey: (do đó phát hành bộ đệm mà giá trị trả lại chỉ).

Nó giống nhau interior pointer issue garbage collection has. Bạn có thể CFRetain/CFRelease bộ giải mã của bạn để kéo dài tuổi thọ của thiết bị, hoặc chỉ cần thêm [decoder self] sau đó trong phương pháp để giữ cho trình duyệt tồn tại cho đến thời điểm đó.

Tôi nghi ngờ bạn cũng có thể giải quyết vấn đề này bằng cách chú thích decoder với __attribute__((objc_precise_lifetime)), nhưng sự hiểu biết của tôi về that attribute có phần hơi mơ hồ.

3

Ví dụ của bạn để lại nhiều biến được đặt câu hỏi bởi bất kỳ người trợ giúp tiềm năng nào. Ví dụ: nếu có điều gì đó sôi nổi với người bỏ lưu trữ này? Bộ nhớ có được quản lý chính xác không?

Tôi có thể tạo lại sự cố mà bạn nhìn thấy và có thể xác nhận sự cố chỉ xảy ra khi bật -O3 và không phải khi nào, ví dụ: Không có gì được chọn để tối ưu hóa. Dưới đây là một đoạn mã bị rơi để loại bỏ các biến bên ngoài như quản lý bộ nhớ của các lập trình viên, v.v. Lưu ý mã bên dưới cố ý giữ lại tất cả các đối tượng để loại bỏ khả năng vụ tai nạn liên quan đến việc phát hành quá ngẫu nhiên, hoặc một bên- ảnh hưởng của việc sử dụng ARC, theo đề nghị của Andy trong câu trả lời khác:.

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]; 
} 

tôi nghĩ rằng điều này mang lại một mô tả ngắn gọn hơn về vấn đề mà bất cứ ai muốn giúp có thể sử dụng như một cơ sở cho việc lặn trong linh cảm của tôi vậy, đến nay đây có phải là lỗi tối ưu trong LLVM 3.0 hay không, nhưng có lẽ ai đó sẽ có một lý thuyết tốt hơn về những gì đang diễn ra.

Điểm mà bạn không đề cập đến trong câu hỏi của mình, nhưng điều tôi nhận thấy trong sự cố trên thiết bị của mình là lỗi được kèm theo đề cập đến lỗi EXC_ARM_DA_ALIGN là lý do ngoại lệ truy cập bị lỗi.Tôi đã google một bài đăng blog mà dường như ám chỉ đến các triệu chứng tương tự và có lẽ gây ra cho đâm như bạn đang nhìn thấy ở đây:

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

Thật vậy, bằng cách thay đổi dòng trên

decodedStruct = *(BBPointI32*)data; 

để

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

Hành vi bị lỗi dường như bị giảm bớt và mã hoạt động như mong đợi.

+0

Tôi không nhận thấy EXC_ARM_DA_ALIGN ở cuối của mình, nhưng yep, đó là thay đổi tôi cũng đã thực hiện. Cảm ơn bạn đã giúp đỡ. –

1

Tôi đã chuyển đến chuỗi này, googling "EXC_ARM_DA_ALIGN" và "EXC_BAD_ACCESS". Không có câu trả lời nào khác giúp tôi, vì lỗi này bị xén vì một thứ tương đối đơn giản. Tôi đã viết:

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

tức là tôi đã để lại một @ trước một chuỗi chữ. Đặt nó trở lại trong giải quyết lỗi.

+0

lol - Tôi chỉ mất 2 giờ để cố gắng theo dõi điều này. Cảm ơn bạn đã đăng giải pháp. – wufoo

Các vấn đề liên quan