Erreur (« '()' n'est pas identique à 'UInt8' ») lors de l'écriture d'octets NSData dans NSOutputStream à l'aide de la fonction d'écriture dans Swift
Question
J'essaie de créer un téléchargement de fichiers asynchrone dans Swift basé sur La méthode d'Erica Sadun.Mais j'en ai besoin pour gérer des fichiers plus gros, alors j'ai trouvé cette réponse sur l'utilisation d'un NSOutputStream au lieu de NSData, logique.
Cependant, je n'arrive pas à le faire fonctionner.J'obtiens cette erreur lorsque j'essaie d'ajouter les octets NSData (dans ma fonction NSURLConnection didReceiveData) à la fonction d'écriture NSOutputStream : '()' is not identical to 'UInt8'
sur cette ligne : bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite)
.
data.bytes
est du genre ConstUnsafePointer<()>
et le .write()
la fonction s'attend à ce que le type soit ConstUnsafePointer<UInt8>
, donc en ce sens, l'erreur est parfaitement logique.Mais comme je suis nouveau sur iOS et bien sûr sur la programmation Swift, je n'arrive pas à comprendre comment résoudre ce problème.
Alors, comment puis-je convertir le data.bytes: ConstUnsafePointer<()>
à ConstUnsafePointer<UInt8>
alt.faire en sorte que cela fonctionne d'une autre manière ?
Mon didReceiveData
fonction:
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)%")
}
}
La solution
Vous pouvez lancer le pointeur avec UnsafePointer()
:
bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)
Il y a aussi un problème dans votre boucle d'écriture, car vous écrivez toujours leinitial octets de l'objet de données dans le flux de sortie.
Cela devrait probablement ressembler à ceci (non testé) :
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
// ...
}
Autres conseils
Je suggérerais de profiter de enumerateByteRangesUsingBlock
, parce que NSData
ne garantit plus que les données sous-jacentes seront conservées dans un seul bloc de mémoire contigu.Par exemple, selon la documentation de didReceiveData
de la NSURLSessionDataDelegate
protocole:
Parce que le
NSData
L'objet est souvent reconstitué à partir d'un certain nombre d'objets de données différents. Dans la mesure du possible, utilisezNSData
c'estenumerateByteRangesUsingBlock:
méthode pour parcourir les données plutôt que d'utiliser labytes
méthode (qui aplatit leNSData
objet dans un seul bloc mémoire).
Ainsi, par exemple, vous pourriez faire une extension de NSOutputStream
qui écrit le contenu d'un 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
}
}
A noter, la technique d'arrêt de l'énumération en cas d'erreur, à savoir stop.initialize(true)
, nécessite Xcode 6 bêta 4 ou version ultérieure.Les versions antérieures de Xcode (et du compilateur associé) utilisaient une construction plus délicate pour mettre à jour la référence booléenne afin d'arrêter l'énumération.