【问题标题】:What is the best way to transport CGPoint between devices?在设备之间传输 CGPoint 的最佳方式是什么?
【发布时间】:2014-09-07 20:46:28
【问题描述】:

我正在使用 swift 开发一个回合制游戏,其中所有内容都必须转换为 NSData。现在我已经被困在“NS-stuff”上很多天了。因为CGPoint不是对象而是struct,所以不能直接转换成NSData,CGPoint里面的CGFloat也是这样。我想做的是:(只是一个想法,不确定它是否有效。)

  1. 将 CGPoint 转换为 2 个不同的浮点数
  2. 使用浮点数创建 NSNummer
  3. 使用 NSNumber 创建 NSData
  4. 在另一台设备中将 NSData 转换回 NSNumber
  5. 将 NSNumber 转换为浮点数
  6. 将它们合并回 CGPoint

这种方式太复杂了。 有没有更好的方法来实现它或任何最佳实践?先谢谢了!!!

PS:不应该考虑 NSKeyedArchiver,因为它的性能。 (不确定!)

【问题讨论】:

  • 虽然您可能认为它“太复杂”并且其他人提出了替代方案,但您描述的解决方案很好。

标签: swift nsdata nsnumber nskeyedarchiver cgpoint


【解决方案1】:

编码CGPoint 的最简单方法是使用NSStringFromCGPointCGPointFromString。这消除了您会面临的所有 32/64 位问题。

这种运输非常便宜(不到 1 个 MTU,所以这并不重要)。唯一可能很昂贵的是字符串解析,几乎可以肯定的是,它被放在网络上的成本所压倒。

但是,如果您有一个非常严格的网络协议并且需要最佳性能,我会将CGPoint 转换为(Float32, Float32),或者更好的是,如果您实际上不需要非整数值,则为(Int32, Int32) .正如@robmayoff 所建议的那样,这很容易编码,假设您不关心字节顺序。不过,借助 Int,您可以轻松地使用 Int.bigEndianInt(bigEndian:) 来处理这个问题。

【讨论】:

    【解决方案2】:

    注意:如果您不需要高性能,您可能最好使用 Rob Napier 的建议 (NSStringFromCGPoint/CGPointFromString),因为它更简单、更易于调试。但是,许多游戏对性能有很高的要求,每个游戏循环需要不止一个 CGPoint。这些是我的假设。

    假设CGFloat 在发送方和接收方上是相同的类型(32 位或 64 位):

    var pointToSend = CGPointMake(100, 200)
    let data = NSData(bytes: &pointToSend, length: MemoryLayout<CGPoint>.size)
    // send data to other device
    
    // receive data on other device
    if (data.length == MemoryLayout<CGPoint>.size) {
        var receivedPoint = UnsafePointer<CGPoint>(data.bytes).memory
    } else {
        // error
    }
    

    如果接收者的CGFloat 可能与发送者的不同:

    typealias DoublePoint = (x: Double, y: Double)
    
    // sender
    var pointToSend = CGPointMake(100, 200)
    var pairToSend = (Double(pointToSend.x), Double(pointToSend.y))
    let data = NSData(bytes: &pointToSend, length: MemoryLayout.size(ofValue: pairToSend))
    
    // receiver
    if (data.length == MemoryLayout<DoublePoint>.size) {
        var receivedPair = UnsafePointer<DoublePoint>(data.bytes).memory
        var receivedPoint = CGPointMake(CGFloat(receivedPair.x), CGFloat(receivedPair.y))
    } else {
        // error
    }
    

    【讨论】:

    • 谢谢。它运作良好。现在我正在研究“UnsafePointer”以了解为什么它被称为“不安全”。 :)
    • 不安全有几个原因。它不提供内存管理,所以如果底层对象消失了,它可能会变成一个悬空指针(这里不会发生这种情况,因为指针会立即被销毁)。它也是不安全的,因为它绕过了类型安全。例如,如果data.bytes 太短,您将在此处读取无效内存并可能崩溃。如果data 实际上并不指向CGPoint,您可以读取垃圾(这对于CGPoint 来说无关紧要,但可能会损坏其他类型的对象)。不是说这个答案没有用;只是不安全。
    • UnsafePointer 允许你做不安全的事情(比如崩溃你的程序或破坏你的堆),因为它绕过了类型系统和引用计数系统。与任何强大的工具一样,您可以安全地使用它。我已更新我的答案以使其更安全。
    • 嘿,sizeofValuesizeof 的功能是什么?以及如何同时转换多个值,例如点和角度?
    • 我已更新我的答案以使用MemoryLayout(在我发布此答案两年后,在 Swift 3.0 中引入)。
    猜你喜欢
    • 2012-12-25
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 2012-12-18
    • 1970-01-01
    • 2021-04-28
    • 2014-08-05
    • 1970-01-01
    相关资源
    最近更新 更多