Fehler („'()' ist nicht identisch mit ‚UInt8‘“) beim Schreiben von NSData-Bytes in NSOutputStream mithilfe der Schreibfunktion in Swift
Frage
Ich versuche, einen asynchronen Dateidownload in Swift basierend auf zu erstellen Erica Saduns Methode.Aber ich brauche es, um größere Dateien zu verarbeiten, also habe ich es gefunden Diese Antwort über die Verwendung eines NSOutputStream anstelle von NSData, macht Sinn.
Allerdings bekomme ich es nicht zum Laufen.Ich erhalte diese Fehlermeldung, wenn ich versuche, die NSData-Bytes (in meiner NSURLConnection didReceiveData-Funktion) zur NSOutputStream-Schreibfunktion hinzuzufügen: '()' is not identical to 'UInt8'
in dieser Zeile: bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite)
.
data.bytes
ist vom Typ ConstUnsafePointer<()>
und das .write()
Die Funktion erwartet den Typ ConstUnsafePointer<UInt8>
, In diesem Sinne macht der Fehler also durchaus Sinn.Aber da ich neu in iOS und natürlich in der Swift-Programmierung bin, kann ich mir nicht vorstellen, wie ich das beheben kann.
Also, wie konvertiere ich das data.bytes: ConstUnsafePointer<()>
Zu ConstUnsafePointer<UInt8>
alt.das auf eine andere Art und Weise zum Laufen bringen?
Mein didReceiveData
Funktion:
func connection(connection: NSURLConnection!, didReceiveData data: NSData!) {
var bytesLeftToWrite: NSInteger = data.length
var bytesWritten: NSInteger = 0
while bytesLeftToWrite > 0 {
bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite)
if bytesWritten == -1 {
break
}
bytesLeftToWrite -= bytesWritten
let responseExpectedlenght: NSNumber = NSNumber(longLong: self.downloadResponse!.expectedContentLength)
let dataLength: NSNumber = NSNumber(long: data.length)
self.downloadProgressPercentage = ((dataLength / responseExpectedlenght) * 100)
println("Downloaded: \(self.downloadProgressPercentage)%")
}
}
Lösung
Sie können den Zeiger mit umwandeln UnsafePointer()
:
bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)
Es gibt auch ein Problem in Ihrer Schreibschleife, weil Sie immer schreibenanfänglich Bytes des Datenobjekts in den Ausgabestream.
Es sollte wahrscheinlich so aussehen (ungetestet):
var bytes = UnsafePointer<UInt8>(data.bytes)
var bytesLeftToWrite: NSInteger = data.length
while bytesLeftToWrite > 0 {
let bytesWritten = self.downloadStream.write(bytes, maxLength: bytesLeftToWrite)
if bytesWritten == -1 {
break // Some error occurred ...
}
bytesLeftToWrite -= bytesWritten
bytes += bytesWritten // advance pointer
// ...
}
Andere Tipps
Ich würde vorschlagen, dass Sie davon Gebrauch machen enumerateByteRangesUsingBlock
, Weil NSData
garantiert nicht mehr, dass die zugrunde liegenden Daten in einem einzigen zusammenhängenden Speicherblock gespeichert werden.Zum Beispiel laut der Dokumentation für didReceiveData
des NSURLSessionDataDelegate
Protokoll:
Weil das
NSData
Objekt wird oft aus einer Reihe verschiedener Datenobjekte zusammengesetzt, wann immer dies möglich istNSData
'SenumerateByteRangesUsingBlock:
Methode zum Durchlaufen der Daten, anstatt die zu verwendenbytes
Methode (die dieNSData
Objekt in einen einzelnen Speicherblock).
So könnten Sie beispielsweise eine Erweiterung vornehmen NSOutputStream
das schreibt den Inhalt von a NSData
:
extension NSOutputStream {
/// Write contents of NSData to `NSOutputStream`
///
/// - parameter data: The `NSData` being written to the stream.
///
/// - returns: The number of bytes written. In case of error, returns -1.
func writeData(data: NSData) -> Int {
var totalBytesWritten = 0
data.enumerateByteRangesUsingBlock() {
buffer, range, stop in
var bytes = UnsafePointer<UInt8>(buffer)
var bytesWritten = 0
var bytesLeftToWrite = range.length
while bytesLeftToWrite > 0 {
bytesWritten = self.write(bytes, maxLength: bytesLeftToWrite)
if bytesWritten < 0 {
stop.initialize(true)
totalBytesWritten = -1
return
}
bytes += bytesWritten
bytesLeftToWrite -= bytesWritten
totalBytesWritten += bytesWritten
}
}
return totalBytesWritten
}
}
Beachten Sie die Technik zum Stoppen der Aufzählung im Fehlerfall, nämlich stop.initialize(true)
, erfordert Xcode 6 Beta 4 oder höher.Frühere Versionen von Xcode (und dem zugehörigen Compiler) verwendeten eine umständlichere Konstruktion zum Aktualisieren der booleschen Referenz, um die Aufzählung zu stoppen.