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
И БУМ!!!
Удачного кодирования!