Запись данных в NSOutputStream в Swift 3

Решение этого вопроса больше не работает со Swift 3.

Больше нет собственности bytes из Data (ранее NSData.

let data = dataToWrite.first!
self.outputStream.write(&data, maxLength: data.count)

С этим кодом я получаю ошибку:

Cannot convert value of type 'Data' to expected argument type 'UInt8'

Как вы можете записать Data в NSOutputStream в Swift 3?


person ahyattdev    schedule 29.06.2016    source источник


Ответы (4)


NSData имел свойство bytes для доступа к байтам. Новый тип значения Data в Swift 3 вместо этого имеет метод withUnsafeBytes(), который вызывает замыкание с указателем на байты.

Итак, вот как вы записываете Data в NSOutputStream (без приведения к NSData):

let data = ... // a Data value
let bytesWritten = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }

Примечания. withUnsafeBytes() — это универсальный метод:

/// Access the bytes in the data.
///
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
public func withUnsafeBytes<ResultType, ContentType>(_ body: @noescape (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType

В приведенном выше вызове и ContentType, и ResultType автоматически выводятся компилятором (как UInt8 и Int), что делает ненужными дополнительные преобразования UnsafePointer().

outputStream.write() возвращает количество фактически записанных байтов. Как правило, вам следует проверить это значение. Это может быть -1, если операция записи не удалась, или меньше data.count при записи в сокеты, каналы или другие объекты с управлением потоком.

person Martin R    schedule 29.06.2016

Мартин Р, спасибо за ответ. Это было основой для полного решения. Вот:

extension OutputStream {

    /// Write String to outputStream
    ///
    /// - parameter string:                The string to write.
    /// - parameter encoding:              The String.Encoding to use when writing the string. This will default to UTF8.
    /// - parameter allowLossyConversion:  Whether to permit lossy conversion when writing the string.
    ///
    /// - returns:                         Return total number of bytes written upon success. Return -1 upon failure.

    func write(_ string: String, encoding: String.Encoding = String.Encoding.utf8, allowLossyConversion: Bool = true) -> Int {
        if let data = string.data(using: encoding, allowLossyConversion: allowLossyConversion) {
            var bytesRemaining = data.count
            var totalBytesWritten = 0

            while bytesRemaining > 0 {
                let bytesWritten = data.withUnsafeBytes {
                    self.write(
                        $0.advanced(by: totalBytesWritten),
                        maxLength: bytesRemaining
                    )
                }
                if bytesWritten < 0 {
                    // "Can not OutputStream.write(): \(self.streamError?.localizedDescription)"
                    return -1
                } else if bytesWritten == 0 {
                    // "OutputStream.write() returned 0"
                    return totalBytesWritten
                }

                bytesRemaining -= bytesWritten
                totalBytesWritten += bytesWritten
            }

            return totalBytesWritten
        }

        return -1
    }
}
person Sergey Markelov    schedule 19.09.2016

Просто используйте это расширение:

Свифт 5

extension OutputStream {
  func write(data: Data) -> Int {
    return data.withUnsafeBytes {
      write($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: data.count)
    }
  }
}

И для InputStream

extension InputStream {
  func read(data: inout Data) -> Int {
    return data.withUnsafeMutableBytes {
      read($0.bindMemory(to: UInt8.self).baseAddress!, maxLength: data.count)
    }
  }
}

Свифт 4

extension OutputStream {
  func write(data: Data) -> Int {
    return data.withUnsafeBytes { write($0, maxLength: data.count) }
  }
}
extension InputStream {
  func read(data: inout Data) -> Int {
    return data.withUnsafeMutableBytes { read($0, maxLength: data.count) }
  }
}
person Dmitry Kozlov    schedule 19.09.2017
comment
У меня возникла аналогичная проблема, и я попытался использовать как расширение OutputStream, так и код let bytesWritten = data.withUnsafeBytes { outputStream.write...., упомянутый ранее. В обоих случаях я получаю -1 байт. Должно быть, я делаю что-то не так. - person Michel; 16.01.2019
comment
@Michel Похоже, с вашим соединением что-то не так. - person Dmitry Kozlov; 24.02.2019
comment
Компиляция показывает эту ошибку в расширении входного потока. Перекрывающиеся доступы к «данным», но модификация требует монопольного доступа; рассмотрите возможность копирования в локальную переменную - person Mark Bridges; 08.07.2021

Data и NSData — это два отдельных класса в Swift 3, а Data не имеет свойства bytes.

Решение состоит в том, чтобы определить data как тип NSData.

let data: NSData = dataToWrite.first!
self.outputStream.write(UnsafePointer<UInt8>(data.bytes), maxLength: data.length)

Согласно Миграция на Swift 2.3 или Swift 3 с Swift 2.2:

Мигратор преобразует большинство случаев использования NSData в новый тип значения Data. Однако существуют определенные методы для NSData, которые работают с UnsafeMutablePointer, в то время как соответствующие методы для Data используют UnsafeMutablePointer. (Например, NSData.getBytes(:length:) более приемлем, чем Data.copyBytes(:length:).) Напоминаем, что расположение типов Swift в памяти не гарантируется.

person ahyattdev    schedule 29.06.2016