我正在尝试基于Erica Sadun的方法在Swift中构建异步文件下载。但是我需要它来处理更大的文件,所以我发现这个关于使用NSOutputStream而不是NSData的答案是有意义的。
然而,我不能让它工作。当我尝试将NSData字节(在我的NSURLConnection didReceiveData函数中)添加到NSOutputStream写函数时,我得到了这个错误:'()' is not identical to 'UInt8'
在这一行:bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite)
。
data.bytes
为ConstUnsafePointer<()>
类型,而.write()
函数期望该类型为ConstUnsafePointer<UInt8>
,因此在这种意义上,错误是完全有意义的。但由于我是iOS新手,当然还有Swift编程新手,我不知道如何解决这个问题。
那么,我如何将data.bytes: ConstUnsafePointer<()>
转换为ConstUnsafePointer<UInt8>
,使其以其他方式工作?
我的didReceiveData
函数:
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)%")
}
}
可以使用UnsafePointer()
:
bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)
在写循环中也有一个问题,因为您总是写初始字节的数据对象到输出流。
它应该看起来像这样(未经测试):
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
// ...
}
我建议您使用enumerateByteRangesUsingBlock
,因为NSData
不再保证底层数据将保存在单个连续内存块中。例如,根据NSURLSessionDataDelegate
协议的didReceiveData
的文档:
因此,例如,您可以对因为
NSData
对象通常是由许多不同的数据对象拼凑在一起的,所以只要可能,使用NSData
的enumerateByteRangesUsingBlock:
方法来迭代数据,而不是使用bytes
方法(它将NSData
对象平铺成单个内存块)。
NSOutputStream
进行扩展,写入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
}
}
注意,在发生错误时停止枚举的技术,即stop.initialize(true)
,需要Xcode 6 beta 4或更高版本。早期版本的Xcode(和相关的编译器)使用了一种更尴尬的结构来更新布尔引用以停止枚举。