2016-03-22 2 views
2

Я делаю кучу работы с [Uint8] массивами. Но мне часто приходится переписывать их как объекты NSData для взаимодействия с инфраструктурами iOS, такими как CoreBluetooth. Так что у меня много кода, который может выглядеть примерно так:Swift: невозможно преобразовать значение типа «Я» в ожидаемый тип аргумента «UnsafePointer <Void> '

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

устать от того, чтобы вставить эту дополнительную let data = ... линии, я думал, я бы просто расширить эти массивы с вычисленным свойством, чтобы сделать работу. Тогда я мог бы просто делать такие вещи, как:

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

Так что это в основном просто сахара-удлинитель. Я не могу расширить массив, но я могу продлить протокол:

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

Который выдает ошибку, которая выглядит как:

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) 
           ^~~~ 

К сожалению, больше я использую Swift - и я действительно люблю некоторые вещи о Swift - тем больше я вспоминаю о своих негативных впечатлениях, пытаясь понять множество неудачных выходных данных компилятора, когда я попробовал свои силы на C++ с большим количеством дженериков и много лет назад. Поэтому, пожалуйста, Оби Ван, помогите мне увидеть свет здесь!

+0

Как вы планируете вызывать расширение как это? Можете ли вы добавить код вызова? – ryantxr

ответ

3

NSData(bytes:, length:) принимает UnsafePointer<Void> в качестве первого параметра, и вы не можете передать произвольное SequenceType здесь.

Вы можете пройти Array, который будет принят в качестве адреса первого элемента массива. Однако не гарантируется, что Array элементы хранятся в непрерывной памяти.

Поэтому:

  • Определить Array расширение вместо Sequence расширения.
  • Используйте метод withUnsafeBufferPointer(), чтобы получить указатель для непрерывного хранения массива.

Возможное решение:

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 в настоящее время не позволяет ограничить расширение массива к конкретному типу:

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

поэтому обобщается на любые элементы соответствующих IntegerType.

Для последовательности можно затем выполнить преобразование в массив первого:

let data = Array(myUInt8sequence).nsdata 
+0

ОК, это работает, но у меня есть больше вопросов, чем раньше! Я думал, что нельзя расширять шаблонные структуры, но нужно расширять протоколы с уточнениями? Но ясно, что я ошибся? Но тогда меня смущает то, что вы используете 'IntegerType', а не' UInt8'? Я попробовал UInt8, и он разозлился, потому что UInt8 был скорее типом, чем протоколом. –

+0

Кроме того, мне не нужно было использовать withUnsafeBufferPointer, чтобы сделать компилятор счастливым. Какое нежелательное поведение произойдет, если я просто пропустил эту оболочку и использовал NSData по умолчанию (bytes: self ...)? –

+1

@TravisGriggs: Начиная с Xcode 7 beta 2 вы можете расширить типы ограниченных массивов. Но вы можете ограничить тип элемента только протоколом, а не конкретным типом, поэтому я выбрал IntegerType. - Найдите интерактивную справку с помощью UnsafeBufferPointer. Он гарантирует, что элементы массива находятся в непрерывном хранилище, что в противном случае не гарантируется. –

1

Это, кажется, что вы хотите.

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

Awesome дополнение. Благодаря! –

Смежные вопросы

 Смежные вопросы