Error (“'()' is not identical to 'UInt

2019-01-26 19:18发布

问题:

I'm trying to build an asynchronous file download in Swift based on the Erica Sadun's method. But I need it to handle bigger files, so I found this answer about using a NSOutputStream instead of NSData, makes sense.

However, I can't get it to work. I get this error when I try adding the NSData bytes (in my NSURLConnection didReceiveData function) to the NSOutputStream write function: '()' is not identical to 'UInt8' on this row: bytesWritten = self.downloadStream.write(data.bytes, maxLength: bytesLeftToWrite).

data.bytes is of the type ConstUnsafePointer<()> and the .write() function expects the type to be ConstUnsafePointer<UInt8>, so in that sense, the error make perfect sense. But since I'm new to iOS and of course Swift programming, I can't get my head around how to fix this.

So, how do I convert the data.bytes: ConstUnsafePointer<()> to ConstUnsafePointer<UInt8> alt. make this work some other way?

My didReceiveData function:

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)%")

    }

}

回答1:

You can cast the pointer with UnsafePointer():

bytesWritten = self.downloadStream.write(UnsafePointer(data.bytes), maxLength: bytesLeftToWrite)

There is also a problem in your write loop, because you always write the initial bytes of the data object to the output stream.

It should probably look similar to this (untested):

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

    // ...
}


回答2:

I'd would suggest availing yourself of enumerateByteRangesUsingBlock, because NSData no longer guarantees that the underlying data will be held in a single contiguous memory block. For example, according to the documentation for didReceiveData of the NSURLSessionDataDelegate protocol:

Because the NSData object is often pieced together from a number of different data objects, whenever possible, use NSData’s enumerateByteRangesUsingBlock: method to iterate through the data rather than using the bytes method (which flattens the NSData object into a single memory block).

Thus, for example, you could do an extension of NSOutputStream that writes the contents of 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
    }

}

Note, the technique of stopping the enumeration in case of error, namely stop.initialize(true), requires Xcode 6 beta 4 or later. Earlier versions of Xcode (and associated compiler) used a more awkward construction for updating the boolean reference to stop the enumeration.