Как да конвертирате Int в NSData в Swift?

В Objective-C използвам следния код за

  1. Преобразувайте променлива Int в NSData, пакет от байтове.

    int myScore = 0;
    NSData *packet = [NSData dataWithBytes:&myScore length:sizeof(myScore)];
    
  2. Използвайте преобразуваната променлива NSData в метод.

    [match sendDataToAllPlayers: 
    packet withDataMode: GKMatchSendDataUnreliable 
    error: &error];
    

Опитах да конвертирам кода на Objective-C в Swift:

var myScore : Int = 0

func sendDataToAllPlayers(packet: Int!,
            withDataMode mode: GKMatchSendDataMode,
            error: NSErrorPointer) -> Bool {

            return true
}

Въпреки това не мога да преобразувам променлива Int в NSData и да я използвам като метод. Как мога да направя това?


person Cesare    schedule 23.02.2015    source източник
comment
stackoverflow.com/a/28662828/2303865   -  person Leo Dabus    schedule 23.02.2015


Отговори (7)


Със Swift 3.x до 5.0:

var myInt = 77
var myIntData = Data(bytes: &myInt, 
                     count: MemoryLayout.size(ofValue: myInt))
person Raphael    schedule 06.04.2017

В съвременните версии на Swift бих направил:

let score = 1000
let data = withUnsafeBytes(of: score) { Data($0) }
e8 03 00 00 00 00 00 00 

И преобразувайте това Data обратно в Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self)
}

Имайте предвид, че когато работите с двоични представяния на числа, особено когато обменяте с някаква отдалечена услуга/устройство, може да искате да направите endianness изрично, напр.

let data = withUnsafeBytes(of: score.littleEndian) { Data($0) }
 e8 03 00 00 00 00 00 00 

И преобразувайте това Data обратно в Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self).littleEndian
}

Срещу големия формат на байтовете, известен също като „мрежов ред на байтовете“:

let data = withUnsafeBytes(of: score.bigEndian) { Data($0) }
 00 00 00 00 00 00 03 e8

И преобразувайте това Data обратно в Int:

let value = data.withUnsafeBytes {
    $0.load(as: Int.self).bigEndian
}

Излишно е да казвам, че ако не искате да се притеснявате за endianness, можете да използвате някакъв установен стандарт като JSON (или дори XML).


За представяне на Swift 2 вижте предишна версия на този отговор.

person Rob    schedule 23.02.2015
comment
IMO такъв data няма да бъде преносим (не трябва да се прехвърля без допълнителни метаданни) между устройства с различен ред на байтове (вижте тук). - person Drux; 25.02.2015
comment
Всъщност съм съгласен. Току-що моделирах отговора си след изпращане на данни до други играчи дискусия в Ръководството за програмиране на Game Center, но изглежда по-разумно да се използва архивиране или plist или нещо друго здрав формат. - person Rob; 26.02.2015
comment
Можете да използвате score.bigEndian или score.littleEndian, за да принудите реда на байтовете да бъде със специфичен край. След това данните ще бъдат съвместими между архитектурите, които могат да се записват на една и да се четат на друга. - person Benjohn; 05.04.2017
comment
Бих предложил bigEndian, тъй като това обикновено се счита за мрежов ред на байтовете. И също така, очевидно, е правилният начин ;-) - person Benjohn; 05.04.2017

Можете да конвертирате по този начин:

var myScore: NSInteger = 0
let data = NSData(bytes: &myScore, length: sizeof(NSInteger))
person David V    schedule 23.02.2015
comment
Благодаря ти много! Може ли да ми кажете как да включа data в моя метод? Получавам data is not a type, когато го кодирам по този начин: func sendDataToAllPlayers(data, - person Cesare; 23.02.2015
comment
Заповядайте :) Съжалявам, не разбирам въпроса ви, какво трябва да направите? - person David V; 23.02.2015
comment
Ако погледнете втория куршум на моя въпрос, има код на функция sendDataToAllPlayers Вече преведох тази функция, но бих искал да включа константата data, която сте декларирали в отговора си, в моя метод Swift. Съжалявам за недоразумението! - person Cesare; 23.02.2015
comment
Изглежда, че отговорът на Роб е това, което търсите, вярно ли е? на пропускам нещо? - person David V; 23.02.2015

За всеки тип цяло число:

extension FixedWidthInteger {
    var data: Data {
        let data = withUnsafeBytes(of: self) { Data($0) }
        return data
    }
}

Пример:

let data = 1.data
person Vyacheslav    schedule 02.12.2020

Swift 5, добавете друга опция.

NSData е стар, все още е в сила

Запис на данни:

let buffer = NSMutableData()
let size = MemoryLayout<UInt>.size
let big = 1000
let small = 10
withUnsafeBytes(of: big, { (p) in
      let bufferPointer = p.bindMemory(to: UInt.self)
      if let address = bufferPointer.baseAddress{
             buffer.append(address, length: size)
      }
})

withUnsafeBytes(of: small, { (p) in
      let bufferPointer = p.bindMemory(to: UInt.self)
      if let address = bufferPointer.baseAddress{
           buffer.append(address, length: size)
      }
 })

Четене на данни:

if let d = buffer.copy() as? Data{
     var big: UInt = 0
     var small: UInt = 0
     let size = MemoryLayout<UInt>.size
     let meta = NSData(data: data)
     meta.getBytes(&big, range: NSRange(location: 0, length: size))
     meta.getBytes(&small, range: NSRange(location: size, length: size))

     print("big:", big, "\nsmall:", small)
    //  big: 1000 
    //  small: 10 
}

Знаете оформлението на паметта, данните, поставени в паметта,

След това ги извадете точно.

unsafe методът е забавен

person dengST30    schedule 05.05.2020

Използвайте този инициализатор, за да конвертирате всеки масив в данни

Data(element: Sequence)

пример:

let myArr     = [12,45,67,898, 100]
let myArrData = Data(myArr)
person jamal zare    schedule 17.11.2020

В зависимост от размера на Int, с който работите, директен подход е да инициирате данни с последователност от UInt8:

let value: Int = 100
let data = Data([UInt8(value)])
// OR
let data = Data([0xFF, 0xFE])
person Brody Robertson    schedule 17.07.2020
comment
Data([UInt8(value)]) ще ви позволи да използвате само числа › 0 и ‹ 256... - person bojan; 06.08.2020
comment
Без съмнение, затова квалифицирах отговора. - person Brody Robertson; 06.08.2020