Я бы также объявил константы свойств. Если вам нужно изменить значения, лучше создать новый объект

аюсь читать голымData в Swift 4struct с помощьюwithUnsafeBytes метод. Проблема

Сетевой UDP-пакет имеет следующий формат:

data: 0102 0A00 0000 0B00 0000

01           : 1 byte : majorVersion      (decimal 01)
02           : 1 byte : minorVersion      (decimal 02)
0A00 0000    : 4 bytes: applicationHostId (decimal 10)
0B00 0000    : 4 bytes: versionNumber     (decimal 11)

Тогда у меня есть расширение наData это занимаетstart иlength байтов для чтения

extension Data {
    func scanValue<T>(start: Int, length: Int) -> T {
        return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
    }
}

Это работает правильно при чтении значений по одному:

// correctly read as decimal "1"
let majorVersion: UInt8 = data.scanValue(start: 0, length: 1)

// correctly read as decimal "2"
let minorVersion: UInt8 = data.scanValue(start: 1, length: 1)

// correctly read as decimal "10"
let applicationHostId: UInt32 = data.scanValue(start: 2, length: 4)

// correctly read as decimal "11"
let versionNumber: UInt32 = data.scanValue(start: 6, length: 4)

Затем я создалstruct который представляет весь пакет следующим образом

struct XPLBeacon {
    var majorVersion: UInt8        // 1 Byte
    var minorVersion: UInt8        // 1 Byte
    var applicationHostId: UInt32  // 4 Bytes
    var versionNumber: UInt32      // 4 Bytes
}

Но когда я читаю данные непосредственно в структуру, у меня возникают некоторые проблемы:

var beacon: XPLBeacon = data.scanValue(start: 0, length: data.count)

// correctly read as decimal "1"
beacon.majorVersion

// correctly read as decimal "2"
beacon.minorVersion

// not correctly read
beacon.applicationHostId

// not correctly read
beacon.versionNumber

Я должен работать, чтобы разобрать всю структуру, как это?

 Leo Dabus28 нояб. 2017 г., 09:36
Обратите внимание, что вам не нужно передавать длину в сигнатуре вашего метода. Вы можете просто использовать размер вашего универсального типаfunc scanValue<T>(start: Int) -> T { return subdata(in: start..<start+MemoryLayout<T>.size).withUnsafeBytes { $0.pointee } }
 Leo Dabus28 нояб. 2017 г., 09:44
и изменить названиеstart вat
 Leo Dabus28 нояб. 2017 г., 09:55
func object<T>(at index: Int) -> T { return self[index..<index+MemoryLayout<T>.size].withUnsafeBytes { $0.pointee } }

Ответы на вопрос(3)

Решение Вопроса

потому что члены структурыподбитый к их естественной границе. Расположение памятиstruct XPLBeacon является

 A B x x C C C C D D D D

где

 offset    member
  0        A       - majorVersion (UInt8)
  1        B       - minorVersion (UInt8)
  2        x x     - padding
  4        C C C C - applicationHostId (UInt32)
  8        D D D D - versionNumber (UInt32)

и набивка вставляется так, чтобыUInt32 члены выравниваются по адресам памяти, кратным их размеру. Это также подтверждается

print(MemoryLayout<XPLBeacon>.size) // 12

(Для получения дополнительной информации о выравнивании в Swift, см.Тип макета).

Если вы читаете все данные в структуру, то байты распределяются следующим образом

 01 02 0A 00 00 00 0B 00 00 00
 A  B  x  x  C  C  C  C  D  D  D  D

что объясняет почемуmajor/minorVersion верны, ноapplicationHostId а такжеversionNumber не правы. Чтение всех членов отдельно от данных является правильным решением.

 Jan28 нояб. 2017 г., 09:38
Я действительно заметил размерMemoryLayout<XPLBeacon>.size и было интересно, почему это12, Это многое объясняет. Я буду использовать пользовательский инициализатор, даже если он создает много кода котельной плиты

extension Data {
    func object<T>(at index: Index) -> T {
        return self[index..<index+MemoryLayout<T>.size].withUnsafeBytes { $0.pointee }
    }
}
struct XPLBeacon {
    var majorVersion: UInt8
    var minorVersion: UInt8
    var applicationHostId: UInt32
    var versionNumber: UInt32

    init(data: Data) {
        var index = data.startIndex
        majorVersion = data.object(at: index)

        index += MemoryLayout.size(ofValue: majorVersion)
        minorVersion = data.object(at: index)

        index += MemoryLayout.size(ofValue: minorVersion)
        applicationHostId = data.object(at: index)

        index += MemoryLayout.size(ofValue: applicationHostId)
        versionNumber = data.object(at: index)
    }
}

Что не является частью этого, конечно, проверка правильности данных. Как уже упоминалось в комментариях, это можно сделать либо с помощью сбойного метода init, либо с помощью Error.

 Leo Dabus28 нояб. 2017 г., 13:52
Я бы также объявил константы свойств. Если вам нужно изменить значения, лучше создать новый объект

Поскольку Swift 3 Data соответствуетRandomAccessCollection, MutableCollection, RangeReplaceableCollection, Таким образом, вы можете просто создать собственный инициализатор для инициализации ваших свойств структуры следующим образом:

struct XPLBeacon {
    let majorVersion, minorVersion: UInt8             // 1 + 1 = 2 Bytes
    let applicationHostId, versionNumber: UInt32      // 4 + 4 = 8 Bytes
    init(data: Data) {
        self.majorVersion      = data[0...0].withUnsafeBytes { $0.pointee }
        self.minorVersion      = data[1...1].withUnsafeBytes { $0.pointee }
        self.applicationHostId = data[2...5].withUnsafeBytes { $0.pointee }
        self.versionNumber     = data[6...9].withUnsafeBytes { $0.pointee }
    }
}
let data = Data([0x01,0x02, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00,0x00])
print(data as NSData)     // "<01020a00 00000b00 0000>\n"

let beacon = XPLBeacon(data: data)
beacon.majorVersion       // 1
beacon.minorVersion       // 2
beacon.applicationHostId  // 10
beacon.versionNumber      // 11
 Jan28 нояб. 2017 г., 09:39
Да, ответ от MartinR это хорошо объясняет. Теперь я понимаю, что случилось. Спасибо за ваш ответ
 user2843428 нояб. 2017 г., 10:45
@LeoDabus, на самом деле, не гарантирует, что данные верны, но гарантирует, что ваш init не завершит работу приложения с фатальной ошибкой во время индекса с диапазоном, но вместо этого предоставит хорошую защелкивающуюся ошибку. Кроме того, если этот тип не является частным шаблоном, всегда полезно проверить внутри init, чем надеяться, что вызывающая сторона проверяла это раньше.
 Leo Dabus28 нояб. 2017 г., 09:38
Конечно, но избежать этого невозможно, MartinR объяснил в своем ответе причину неудачи. Проверьте мой комментарий также выше, как вы можете улучшить свой метод расширения,
 Jan28 нояб. 2017 г., 09:37
Это, конечно, работает, но я пытался избежать этого, так как он создает много кода для котельной пластины, но, похоже, это единственный способ.
 Jan28 нояб. 2017 г., 12:12
Я специально не упомянул, как я получаю данные и действительны ли они или нет, чтобы сосредоточиться на этом вопросе. Здесь мы предполагаем, что данные имеют правильный размер.

Ваш ответ на вопрос