2016-03-22 13 views
2

konvertiert werden Ich mache eine Menge Arbeit mit [Uint8] Arrays. Aber ich muss diese oft als NSData Objekte umwandeln, um mit iOS-Frameworks wie CoreBluetooth zu interagieren. Also habe ich eine Menge Code haben, der etwas aussehen könnte:Swift: Wert des Typs 'Self' kann nicht in den erwarteten Argumenttyp 'UnsafePointer <Void>'

var input:[UInt8] = [0x60, 0x0D, 0xF0, 0x0D] 
let data = NSData(bytes: input, length: input.count) 

müde zu müssen, fügen Sie diese zusätzlichen let data = ... Linie, ich dachte, ich würde nur diese Arrays mit einer berechneten Eigenschaft erweitern die Arbeit zu tun. Dann könnte ich einfach Dinge tun wie:

aBluetoothPeriperal.write(myBytes.nsdata, ...) 

So ist es im Grunde nur Erweiterungszucker. Ich kann nicht Array erweitern, aber ich kann ein Protokoll erweitern:

extension SequenceType where Generator.Element == UInt8 { 
    var nsdata:NSData { 
     return NSData(bytes: self, length: self.count) 
    } 
} 

, die einen Fehler erzeugt, der wie folgt aussieht:

Playground execution failed: MyPlayground.playground:3:24: error: cannot convert value of type 'Self' to expected argument type 'UnsafePointer<Void>' (aka 'UnsafePointer<()>') 
       return NSData(bytes: self, length: self.count) 
           ^~~~ 

Leider je mehr ich Swift verwenden - und ich mag Einige Dinge über Swift - je mehr erinnere ich mich an meine negativen Erfahrungen mit dem Versuch, Unmengen von nicht hilfreichem Compiler-Output zu verstehen, als ich vor vielen Jahren mit C++ und vielen Generika ausprobiert habe. Also bitte, Obi Wan, hilf mir, das Licht hier zu sehen!

+0

Wie planen Sie eine solche Nebenstelle anzurufen? Können Sie hinzufügen, wie der aufrufende Code aussehen würde? – ryantxr

Antwort

3

NSData(bytes:, length:) nimmt einen UnsafePointer<Void> als ersten Parameter, und Sie können hier kein beliebiges SequenceType übergeben.

Sie können eineArray geben, die als die Adresse des ersten Feldelement übergeben werden würde. Es ist jedoch nicht garantiert, dass Array Elemente im zusammenhängenden Speicher gespeichert sind.

Deshalb:

  • Definieren Sie eine Array Erweiterung anstelle einer Sequence Erweiterung.
  • Verwenden Sie die withUnsafeBufferPointer()-Methode, um einen Zeiger auf zusammenhängenden Array-Speicher zu erhalten.

Mögliche Lösung:

extension Array where Element : IntegerType { 
    var nsdata : NSData { 
     return self.withUnsafeBufferPointer { 
      NSData(bytes: $0.baseAddress, length: self.count * strideof(Element)) 
     } 
    } 
} 

let input:[UInt8] = [0x60, 0x0D, 0xF0, 0x0D] 
print(input.nsdata) // <600df00d> 

Swift unterstützt derzeit erlauben kein Array Erweiterung auf einen konkreten Typ zu beschränken:

extension Array where Element == UInt8 { } 
// error: same-type requirement makes generic parameter 'Element' non-generic 

daher wird verallgemeinert keine Elemente mit IntegerType entspricht.

Für eine Sequenz Sie könnten dann zunächst eine Umwandlung in ein Array tun:

let data = Array(myUInt8sequence).nsdata 
+0

OK, das funktioniert, aber ich habe mehr Fragen als zuvor! Ich dachte, man könnte Templates nicht erweitern, sondern Protokolle mit Erweiterungen erweitern? Aber da lag ich eindeutig falsch? Aber was verwirrt mich dann, dass du "IntegerType" anstelle von "UInt8" verwendest? Ich habe versucht, UInt8, und es wurde verrückt, weil UInt8 ein Typ und kein Protokoll war. –

+0

Außerdem musste ich den withUnsafeBufferPointer nicht verwenden, um den Compiler glücklich zu machen. Welches unerwünschte Verhalten würde auftreten, wenn ich nur diesen Wrapper überspringe und die Standard-NSData (bytes: self ...) verwendet? –

+1

@TravisGriggs: Ab Xcode 7 Beta 2 können Sie eingeschränkte Array-Typen erweitern. Aber Sie können den Elementtyp nur auf ein Protokoll und nicht auf einen konkreten Typ beschränken, deshalb habe ich IntegerType gewählt. - Suchen Sie in der Online-Hilfe von withUnsafeBufferPointer. Es stellt sicher, dass die Array-Elemente in zusammenhängendem Speicher sind, der ansonsten nicht garantiert ist. –

1

Dies scheint zu tun, was Sie wollen.

protocol ByteOnly: IntegerType {} 
extension UInt8: ByteOnly {} 

extension Array where Element: ByteOnly { 
    var n : NSData { return NSData(bytes: self, length: self.count) } 
} 
// does it work with UInt8 
var input5:[UInt8] = [0x61, 0x0D, 0xF1, 0x0D] 
let data5 = input5.n // YES 

// does it work on ints? 
var ints: [Int] = [3,4,5,6,7,8] 
let idata5 = ints.n // no 
+0

Super Ergänzung. Vielen Dank! –

Verwandte Themen