EXC_ARM_DA_ALIGN при чтении из NSData в Swift

У меня есть следующий класс:

class RawDataArray {
    var data: NSData!

    init(filePath: String) {
        data = NSData(contentsOfFile: filePath)
    }

    func read<T>(offset: Int) -> T {
        return UnsafePointer<T>(data.bytes + offset).memory
    }
}

который я использую в своем приложении для iOS, чтобы читать из двоичного файла в произвольном формате. Например, чтобы прочитать Int по смещению 5 я использую:

let foo = rawData.read(5) as Int

Это работает в симуляторе, на моем iPad Air, и прошло обзор для бета-тестирования. Тем не менее, у моих внешних тестеров есть iPad 2 и 4, и они получают EXC_ARM_DA_ALIGN ошибки.

Я не могу изменить структуру входного файла. Есть ли способ исправить read функция, чтобы убедиться, что объекты построены из правильно выровненных областей памяти?

2 ответа

Решение

Да, чтение не выровненных данных может не работать на некоторых архитектурах. Безопасный метод - скопировать байты в (правильно выровненную) переменную.

Поэтому первая идея заключается в том, чтобы написать обобщенную функцию:

func read<T>(offset: Int) -> T {
    var value : T
    memcpy(&value, data.bytes + offset, UInt(sizeof(T)))
    return value
}

Однако это не компилируется, потому что переменная valueдолжен быть инициализирован перед использованием, и не существует конструктора по умолчанию, который можно было бы использовать для инициализации полной универсальной переменной.

Если мы ограничим метод типами, которые могут быть инициализированы с 0 тогда мы получим это:

func read<T : IntegerLiteralConvertible>(offset: Int) -> T {
    var value : T = 0
    memcpy(&value, data.bytes + offset, UInt(sizeof(T)))
    return value
}

Это работает для всех целочисленных типов и типов с плавающей точкой.

Для общего случая можно использовать идею из /questions/44997828/swift-peredacha-neinitsializirovannoj-strukturyi-c-v-importirovannuyu-funktsiyu-c/44997848#44997848:

func read<T>(offset: Int) -> T {
    // Allocate space:
    let ptr = UnsafeMutablePointer<T>.alloc(1)
    // Copy data:
    memcpy(ptr, data.bytes + offset, UInt(sizeof(T)))
    // Retrieve the value as a new variable: 
    var item = ptr.move()
    // Free the allocated space
    ptr.dealloc(1)
    return item
}

Swift позволяет перегружать функции разными типами возвращаемых значений, поэтому вы можете определить оба метода, и компилятор автоматически выберет более ограничительный.

ДЛЯ ВСЕХ РАЗРАБОТЧИКОВ SWIFT, КОТОРЫЕ ПРОЧИТАЮТ

У меня был другой случай с тем же типом сбоя!

Если ваш проект определен для (старой?) Быстрой версии, но вы ссылаетесь на какао-боб (в моем случае) с другой (более новой?) Быстрой версией в настройках сборки - вы получите этот сбой даже тогда, когда вы просто пытаюсь установить значение публичного поля в обычной быстрой структуре.

Трассировка стека ничего вам не скажет и может показать неправильное место (например, свободная память CAAnimation)

В моем случае мой проект был в Swift 4, а мой модуль (SwiftMessages) был Swift 4.2

И БУМ!!!

Удачного кодирования!

Другие вопросы по тегам