iOS 12.x Swift 5
I am using sockets to talk to another box and I wrote this code which I think makes sense. but I wanted to check with an expert. I ask cause I think I have some sort of memory leak with this app. And I suspect pointers, as you do. I am watching the memory it uses slowly increase as I am running it.
func sendMessage(message: String) {
let data = message.data(using: String.Encoding.utf8, allowLossyConversion: false)!
let dataMutablePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
//Copies the bytes to the Mutable Pointer
dataMutablePointer.initialize(to: 0)
data.copyBytes(to: dataMutablePointer, count: data.count)
//Cast to regular UnsafePointer
let dataPointer = UnsafePointer<UInt8>(dataMutablePointer)
//Your stream
outputStream.write(dataPointer, maxLength: data.count)
defer {
dataMutablePointer.deinitialize(count: data.count)
}
}
Does this code look sane? Could it be the source of my memory leak? Do I need to deinitalize this pointer?
Could it be the source of my memory leak?
Yes, surely.
Do I need to deinitalize this pointer?
As commented by bscothern, you need to deinitalize the pointer if the Pointee is non-trivial type. But in case of UInt8, it is not mandatory.
But you need to deallocate the pointer eventually.
Your code would be something like this:
func sendMessage(message: String) {
let data = message.data(using: String.Encoding.utf8, allowLossyConversion: false)!
let dataMutablePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)
defer {
dataMutablePointer.deallocate()
}
//Copies the bytes to the Mutable Pointer
dataMutablePointer.initialize(to: 0)
defer {
//This is not required, but you can put `deinitialize` here if you prefer
dataMutablePointer.deinitialize(count: data.count)
}
data.copyBytes(to: dataMutablePointer, count: data.count)
//Cast to regular UnsafePointer
let dataPointer = UnsafePointer<UInt8>(dataMutablePointer)
//Your stream
outputStream.write(dataPointer, maxLength: data.count)
}
defer defers the execution of its block until the end of the enclosing block, so dataMutablePointer.deallocate() will be executed after outputStream.write(dataPointer, maxLength: data.count) is finished.
But I would write something equivalent without copying data:
func sendMessage(message: String) {
let data = message.data(using: String.Encoding.utf8, allowLossyConversion: false)!
data.withUnsafeBytes {bytes in
let dataPointer = bytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
//Your stream
outputStream.write(dataPointer, maxLength: data.count)
}
}
Or, more simply:
func sendMessage(message: String) {
//Your stream
outputStream.write(message, maxLength: message.utf8.count)
}
Related
Now facing some challenges using CoreBlueTooth L2CAP channel. In order to better understand how things work. I have taken the L2CapDemo (master) (https://github.com/paulw11/L2CapDemo) from GitHub and tried to experiment with it. Here is what I have done, along with one question.
In have replaced the sendTextTapped function, with this one :
#IBAction func sendTextTapped(_ sender: UIButton) {
guard let ostream = self.channel?.outputStream else {
return
}
var lngStr = "1234567890"
for _ in 1...10 {lngStr = lngStr + lngStr}
let data = lngStr.data(using: .utf8)!
let bytesWritten = data.withUnsafeBytes { ostream.write($0, maxLength: data.count) }
print("bytesWritten = \(bytesWritten)")
print("WR = \(bytesWritten) / \(data.count)")
}
And the execution result is:
bytesWritten = 8192
WR = 8192 / 10240
That allows me to see what happens in the case where bytesWritten < data.count.
In other words, all the bytes cannot be sent over in one chunk.
Now comes the question. The problem is I see nothing, the bytes left over seems to be just ignored.
I want to know what to do if I do not want to ignore those bytes. What is the way to care about the rest of the bytes? There will be cases where we will need to transfer tens of thousands or even hundreds of thousands of bytes.
You simply need to make a note of how many characters were sent, remove those from the data instance and then when you get a delegate callback indicating space is available in the output stream, send some more.
For example, you could add a couple of properties to hold the queued data and a serial dispatch queue to ensure thread-safe access to that queue:
private var queueQueue = DispatchQueue(label: "queue queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem, target: nil)
private var outputData = Data()
Now, in the sendTextTapped function you can just add the new data to the output queue:
#IBAction func sendTextTapped(_ sender: UIButton) {
var lngStr = "1234567890"
for _ in 1...10 {lngStr = lngStr + lngStr}
let data = lngStr.data(using: .utf8)!
self.queue(data:data)
}
the queue(data:) function adds the data to the outputData object in a thread-safe manner and calls send()
private func queue(data: Data) {
queueQueue.sync {
self.outputData.append(data)
}
self.send()
}
send() ensures that the stream is connected, there is data to send and there is space available in the output stream. If all is OK then it sends as many bytes as it can. The sent bytes are then removed from output data (again in a thread safe manner).
private func send() {
guard let ostream = self.channel?.outputStream, !self.outputData.isEmpty, ostream.hasSpaceAvailable else{
return
}
let bytesWritten = outputData.withUnsafeBytes { ostream.write($0, maxLength: self.outputData.count) }
print("bytesWritten = \(bytesWritten)")
queueQueue.sync {
if bytesWritten < outputData.count {
outputData = outputData.advanced(by: bytesWritten)
} else {
outputData.removeAll()
}
}
}
The final change is to call send() in response to a .hasSpaceAvailable stream event:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.openCompleted:
print("Stream is open")
case Stream.Event.endEncountered:
print("End Encountered")
case Stream.Event.hasBytesAvailable:
print("Bytes are available")
case Stream.Event.hasSpaceAvailable:
print("Space is available")
self.send()
case Stream.Event.errorOccurred:
print("Stream error")
default:
print("Unknown stream event")
}
}
You can see the modified code in the largedata branch of the example
I have an app that pulls data from a socket using the Stream class.
Everything appears to work as expected, I am receiving data in the delegate method:
func stream(_ Stream: Stream, handle eventCode: Stream.Event)
The challenge I am having is converting the data into useful information. There is an Android app that does what I am trying to accomplish using a ByteBuffer.
This ByteBuffer iterates through the data at intervals of 4 and parses the data into the required data types using methods like getFloat and getInt.
Basically I want to loop through the InputStream data and retrieve all the values in the stream. Each value is always 4 bytes in length.
How can I do this using Swift 3?
I got it working. If anyone else needs to do something similar, here is my code:
func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
if aStream === inputStream {
switch eventCode {
case Stream.Event.errorOccurred:
print("input: ErrorOccurred: \(String(describing: aStream.streamError))")
case Stream.Event.openCompleted:
print("input: OpenCompleted")
case Stream.Event.hasBytesAvailable:
print("input: HasBytesAvailable")
var index = 0 //this index is used to map the value from bytes below. For example, 0 is date
var buffer = [UInt8](repeating: 0, count: 4) //this breaks the bytes into groups of 4 bytes each
while (inputStream?.hasBytesAvailable)!{
if let length = inputStream?.read(&buffer, maxLength: buffer.count) {
if(length > 0){
let data = Data(bytes: buffer)
let UINT32 = UInt32(littleEndian: data.withUnsafeBytes { $0.pointee })
let float = data.withUnsafeBytes { $0.pointee } as Float
let int = data.withUnsafeBytes { $0.pointee } as Int
//Do something with the values, map to an object, etc
index += 1 //increase the index to know what the next 4 bytes should be mapped as
}
}
}
break
default:
break
}
} ...
With Swift 3 leaning towards Data instead of [UInt8], I'm trying to ferret out what the most efficient/idiomatic way to encode/decode swifts various number types (UInt8, Double, Float, Int64, etc) as Data objects.
There's this answer for using [UInt8], but it seems to be using various pointer APIs that I can't find on Data.
I'd like to basically some custom extensions that look something like:
let input = 42.13 // implicit Double
let bytes = input.data
let roundtrip = bytes.to(Double) // --> 42.13
The part that really eludes me, I've looked through a bunch of the docs, is how I can get some sort of pointer thing (OpaquePointer or BufferPointer or UnsafePointer?) from any basic struct (which all of the numbers are). In C, I would just slap an ampersand in front of it, and there ya go.
Note: The code has been updated for Swift 5 (Xcode 10.2) now. (Swift 3 and Swift 4.2 versions can be found in the edit history.) Also possibly unaligned data is now correctly handled.
How to create Data from a value
As of Swift 4.2, data can be created from a value simply with
let value = 42.13
let data = withUnsafeBytes(of: value) { Data($0) }
print(data as NSData) // <713d0ad7 a3104540>
Explanation:
withUnsafeBytes(of: value)
invokes the closure with a buffer pointer covering the raw bytes of the value.
A raw buffer pointer is a sequence of bytes, therefore Data($0) can be used to create the data.
How to retrieve a value from Data
As of Swift 5, the withUnsafeBytes(_:) of Data invokes the closure with an “untyped” UnsafeMutableRawBufferPointer to the bytes. The load(fromByteOffset:as:) method the reads the value from the memory:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
let value = data.withUnsafeBytes {
$0.load(as: Double.self)
}
print(value) // 42.13
There is one problem with this approach: It requires that the memory is property aligned for the type (here: aligned to a 8-byte address). But that is not guaranteed, e.g. if the data was obtained as a slice of another Data value.
It is therefore safer to copy the bytes to the value:
let data = Data([0x71, 0x3d, 0x0a, 0xd7, 0xa3, 0x10, 0x45, 0x40])
var value = 0.0
let bytesCopied = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
assert(bytesCopied == MemoryLayout.size(ofValue: value))
print(value) // 42.13
Explanation:
withUnsafeMutableBytes(of:_:) invokes the closure with a mutable buffer pointer covering the raw bytes of the value.
The copyBytes(to:) method of DataProtocol (to which Data conforms) copies bytes from the data to that buffer.
The return value of copyBytes() is the number of bytes copied. It is equal to the size of the destination buffer, or less if the data does not contain enough bytes.
Generic solution #1
The above conversions can now easily be implemented as generic methods of struct Data:
extension Data {
init<T>(from value: T) {
self = Swift.withUnsafeBytes(of: value) { Data($0) }
}
func to<T>(type: T.Type) -> T? where T: ExpressibleByIntegerLiteral {
var value: T = 0
guard count >= MemoryLayout.size(ofValue: value) else { return nil }
_ = Swift.withUnsafeMutableBytes(of: &value, { copyBytes(to: $0)} )
return value
}
}
The constraint T: ExpressibleByIntegerLiteral is added here so that we can easily initialize the value to “zero” – that is not really a restriction because this method can be used with “trival” (integer and floating point) types anyway, see below.
Example:
let value = 42.13 // implicit Double
let data = Data(from: value)
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = data.to(type: Double.self) {
print(roundtrip) // 42.13
} else {
print("not enough data")
}
Similarly, you can convert arrays to Data and back:
extension Data {
init<T>(fromArray values: [T]) {
self = values.withUnsafeBytes { Data($0) }
}
func toArray<T>(type: T.Type) -> [T] where T: ExpressibleByIntegerLiteral {
var array = Array<T>(repeating: 0, count: self.count/MemoryLayout<T>.stride)
_ = array.withUnsafeMutableBytes { copyBytes(to: $0) }
return array
}
}
Example:
let value: [Int16] = [1, Int16.max, Int16.min]
let data = Data(fromArray: value)
print(data as NSData) // <0100ff7f 0080>
let roundtrip = data.toArray(type: Int16.self)
print(roundtrip) // [1, 32767, -32768]
Generic solution #2
The above approach has one disadvantage: It actually works only with "trivial"
types like integers and floating point types. "Complex" types like Array
and String have (hidden) pointers to the underlying storage and cannot be
passed around by just copying the struct itself. It also would not work with
reference types which are just pointers to the real object storage.
So solve that problem, one can
Define a protocol which defines the methods for converting to Data and back:
protocol DataConvertible {
init?(data: Data)
var data: Data { get }
}
Implement the conversions as default methods in a protocol extension:
extension DataConvertible where Self: ExpressibleByIntegerLiteral{
init?(data: Data) {
var value: Self = 0
guard data.count == MemoryLayout.size(ofValue: value) else { return nil }
_ = withUnsafeMutableBytes(of: &value, { data.copyBytes(to: $0)} )
self = value
}
var data: Data {
return withUnsafeBytes(of: self) { Data($0) }
}
}
I have chosen a failable initializer here which checks that the number of bytes provided
matches the size of the type.
And finally declare conformance to all types which can safely be converted to Data and back:
extension Int : DataConvertible { }
extension Float : DataConvertible { }
extension Double : DataConvertible { }
// add more types here ...
This makes the conversion even more elegant:
let value = 42.13
let data = value.data
print(data as NSData) // <713d0ad7 a3104540>
if let roundtrip = Double(data: data) {
print(roundtrip) // 42.13
}
The advantage of the second approach is that you cannot inadvertently do unsafe conversions. The disadvantage is that you have to list all "safe" types explicitly.
You could also implement the protocol for other types which require a non-trivial conversion, such as:
extension String: DataConvertible {
init?(data: Data) {
self.init(data: data, encoding: .utf8)
}
var data: Data {
// Note: a conversion to UTF-8 cannot fail.
return Data(self.utf8)
}
}
or implement the conversion methods in your own types to do whatever is
necessary so serialize and deserialize a value.
Byte order
No byte order conversion is done in the above methods, the data is always in
the host byte order. For a platform independent representation (e.g.
“big endian” aka “network” byte order), use the corresponding integer
properties resp. initializers. For example:
let value = 1000
let data = value.bigEndian.data
print(data as NSData) // <00000000 000003e8>
if let roundtrip = Int(data: data) {
print(Int(bigEndian: roundtrip)) // 1000
}
Of course this conversion can also be done generally, in the generic
conversion method.
You can get an unsafe pointer to mutable objects by using withUnsafePointer:
withUnsafePointer(&input) { /* $0 is your pointer */ }
I don't know of a way to get one for immutable objects, because the inout operator only works on mutable objects.
This is demonstrated in the answer that you've linked to.
In my case, Martin R's answer helped but the result was inverted. So I did a small change in his code:
extension UInt16 : DataConvertible {
init?(data: Data) {
guard data.count == MemoryLayout<UInt16>.size else {
return nil
}
self = data.withUnsafeBytes { $0.pointee }
}
var data: Data {
var value = CFSwapInt16HostToBig(self)//Acho que o padrao do IOS 'e LittleEndian, pois os bytes estavao ao contrario
return Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
}
The problem is related with LittleEndian and BigEndian.
I am trying to send the following structure from one player to another:
struct GamePacket {
var action: String
var pointArray: [CGPoint]
}
I'm having a hard time figuring out the correct way to convert the GamePacket to Data and back again.
Here is the code I have so far for sending packets:
func send(packet: GamePacket) {
//convert GamePacket to Data here
let data = Data.init()
var remotePlayerArray = [GKPlayer]()
if let currentPlayer = self.currentPlayer, let match = self.match, let playerArray = self.match?.players {
for player in playerArray {
if player != currentPlayer {
remotePlayerArray.append(player)
}
}
do {
try match.send(data, to: remotePlayerArray, dataMode: GKMatchSendDataMode.reliable)
}
catch {
print("connectionError")
}
}
}
And the code for receiving:
func match(_ match: GKMatch, didReceive data: Data, fromRemotePlayer player: GKPlayer) {
//convert Data to GamePacket here
}
From some samples written in ObjectiveC, I managed to convert the GamePacket to Data using something simmilar to the following:
let data = NSData(bytes: &packet, length: MemoryLayout<GamePacket>.size) as Data
However, I can't figure out how to convert the Data back to a GamePacket on the receiving end, nor am I sure this is the correct way to do it.
Any help is greatly apreciated. Thank you.
Use Codable
struct GamePacket: Codable {
var action: String
var pointArray: [CGPoint]
}
Then you can convert to Data easily:
func save<T: Encodable>(_ item: T, to url: URL) throws -> Data {
let encoder = JSONEncoder()
return try encoder.encode(item)
}
func load<T: Decodable>(from data:Data) throws -> T {
let decoder = JSONDecoder()
let item = try decoder.decode(T.self, from: data)
return item
}
A quick and dirty solution would be something like this:
func encodeGamePacket(packet: GamePacket) -> NSData {
return NSData(bytes: &gamePacket, length: MemoryLayout<GamePacket>.size)
}
func decodeGamePacket(data: NSData) -> GamePacket? {
var tempBuffer:GamePacket? = nil
data.getBytes(&tempBuffer, length: MemoryLayout<GamePacket>.size)
return tempBuffer
}
I have not messed with direct addresses myself under swift yet, so I am not entirely sure whether this is the best approach. Note that I used an optional return type, you can design this differently in your code (maybe add some checks, unwrap the variable and return it or throw an exception when the checks fail).
Alternatively you could design a method that writes your GamePacket into a String (for readability, for example), which you can in turn transform into NSData (String has a data method) or you turn GamePacket into an NSCoding compliant class that offers methods to convert itself into NSData as well.
Code that was previously working in Swift 2.2 is now throwing the following error in Swift 3:
Here is my code:
let tempData: NSMutableData = NSMutableData(length: 26)!
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes:data.bytes)
What should I replace "data.bytes" with to fix the error? I've tried implementing 'withUnsafeBytes' and had a look at Apple's documentation, but can't get my head around it!
Assuming that data has type Data, the following should work:
let tempData: NSMutableData = NSMutableData(length: 26)!
data.withUnsafeBytes {
tempData.replaceBytes(in: NSMakeRange(0, data.count), withBytes: $0)
}
using the
/// Access the bytes in the data.
///
/// - warning: The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
public func withUnsafeBytes<ResultType, ContentType>(_ body: #noescape (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType
method of Data. Inside the closure $0 is a UnsafePointer<Void>
to the bytes (UnsafeRawPointer in Xcode 8 beta 6).
Use this
:
let data:Data = Data()
let myDataBytes = data.withUnsafeBytes {_ in
UnsafePointer<UInt8>(data.withUnsafeBytes({
$0
}))
}
let writtenBytes = writeStream.write(.init(myDataBytes), maxLength: data.count)