Whether we chose to access the contents of a file as a whole or in part, we will end up with an NSData
object with some bytes in, so how do you get at the bytes ?
As before the answer is, in exactly the same way as you do in Objective-C.
So that would be like this then ?
let bytes = data.bytes
It would.
The similarities between doing things in Swift and Objective-C extend to the type of the property bytes
.
In Objective-C it is declared like this
@property(readonly) const void *bytes
and in Swift like this
var bytes: UnsafePointer<Void> { get }
So what we have got hold of is something with the type
UnsafePointer<Void>
which is the equivalent of
const void*
and it is about as useful, which is to say, not very.
If we do this
let b = bytes[0]
then the compiler will helpfully volunteer the warning
Constant 'b' inferred to have type 'Void' which may be unexpected
Possibly not unexpected, it is an ‘unsafe pointer’ to ‘Void’ after all, but definitely of limited utility.
An empty tuple, for that is what a ‘Void’ is of course, specialises in representing nothing, a job at which it excels. but it makes for a very unconvincing byte.
What we want is a
UnsafePointer<UInt8>
in the same way that we would want a
const uint8_t*
or something in Objective-C.
In Objective-C you do this to get one
const uint8_t* bytes = (const uint8_t*)data.bytes;
and in Swift you do this
let bytes = UnsafePointer<UInt8>(data.bytes)
Once you have one, you can access the byte to which it ‘points’ directly
let b = bytes.memory
or by using a subscript
let c = bytes[1]
Also, just as you can in Objective-C, you can ‘walk’ right off the end of the associated memory because it really is an ‘unsafe’ pointer.
...
let data = NSData()
let bytes = UnsafePointer<UInt8>(data.bytes)
for i in 0 ..< 16
{
println(bytes[i])
}
...
at which point everything may come to a grinding halt, but then again it may not, it all depends.
The other way to get at the bytes in an NSData
object is, needless to say, exactly the same as the other you would do it in
Objective-C, viz.
let bytes = UnsafeMutablePointer<UInt8>.alloc(length)
data.getBytes(bytes, length:data.length)
If you are not happy walking off the end of other people’s memory and prefer walking off the end of your own, this is the option for you.
The memory returned by the call to alloc
is not managed and must be explicitly freed by a call to dealloc
.
bytes.dealloc(length)
The allocated memory is also not initialized to anything in particular and especially not to zero.
In Swift an
UnsafeMutablePointer<T>
is to an
UnsafePointer<T>
as, in Objective-C,
T*
is to
const T*
so you can modify the memory an UnsafeMutablePointer<T>
‘points at’ directly
bytes.memory = UInt8(length)
or using a subscript
bytes[8] = UInt8(length)
As well as the subscript functions UnsafePointer<T>
and UnsafeMutablePointer<T>
types support a variety of operators.
For example
let bytes = UnsafeMutablePointer<UInt8>.alloc(16)
for i in 0 ..< 16
{
bytes[i] = UInt8(i)
}
var p = bytes
for i in 0 ..< 8
{
println(p.memory)
p += 2
}
let end = bytes + 8
p = bytes
while p < end
{
println(p++.memory)
}
p = bytes + 7
while p >= bytes
{
println(p--.memory)
}
The ‘mutating’ operators pre/post decrement/increment etc. can only be used if the ‘pointer’ is referenced from a mutable variable.
You can create an UnsafePointer<T>
from an UnsafeMutablePointer<T>
let bytes = UnsafeMutablePointer<UInt8>.alloc(16)
let immutable = UnsafePointer<UInt8>(bytes)
and even vice-versa
let mutable = UnsafeMutablePointer<UInt8>(data.bytes)
which is a bit worrying but then the clue is in the name. UnsafePointer<T>
s and UnsafeMutablePointer<T>
s, are ‘unsafe’.
Copyright (c) 2014 By Simon Lewis. All Rights Reserved.
Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.
Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.
Leave a comment