简体   繁体   English

如何将包含字符串的消息结构作为NSData发送给Game Center

[英]How to send message struct containing a string as NSData for Game Center

I am trying to create a multiplayer game that will send moves between players using Game Center. 我正在尝试创建一个多人游戏,该游戏将使用Game Center在玩家之间传送动作。 I'm still learning a lot about programming, so please excuse me if my question is ill-formed. 我仍然在学习编程方面的很多知识,所以,如果我的问题格式错误,请原谅。 Also, I am not very familiar with Obj-C, so a Swift answer would be great. 另外,我对Obj-C不太熟悉,因此Swift的回答会很棒。

In my toy program to try and teach myself, I am trying to follow the strategy used by Shayne Meyer using the GameKitHelper class here: https://github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer 在我的玩具程序中尝试自我教,的过程中,我尝试通过此处的GameKitHelper类遵循Shayne Meyer所使用的策略: https : //github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer

Using this approach, Shayne sends messages to other players online using structs sent as NSData. 使用这种方法,Shayne使用NSData发送的结构将消息在线发送给其他玩家。 I am able to send integers (eg, the ILoveYou message) but not messages that carry a string property (eg, the Thanks message). 我能够发送整数(例如ILoveYou消息),但不能发送带有字符串属性的消息(例如,感谢消息)。 In this latter case I get "Thread 1: EXC_BAD_ACCESS(code=1, address=0x78674100)" at the line "var messageThanks = UnsafePointer,MesssageThanks>(data.bytes).memory" 在后一种情况下,我在“ var messageThanks = UnsafePointer,MesssageThanks>(data.bytes).memory”行得到“线程1:EXC_BAD_ACCESS(code = 1,地址= 0x78674100)”。

Eventually, I would like to send game moves that provide both strings and integers together. 最终,我想发送同时提供字符串和整数的游戏动作。 How does one send a message struct as NSData when properties also include a string? 当属性还包含字符串时,如何将消息结构作为NSData发送? Secondly, I would be appreciative if someone could help me understand fundamentally what is going on when the data is packaged and how what UnsafePointer is doing as it related to sending data via Game Center. 其次,如果有人可以帮助我从根本上理解打包数据时正在发生的事情以及UnsafePointer与通过Game Center发送数据有关的情况,我将不胜感激。

Thank you. 谢谢。 Cliff 悬崖

enum MessageType: Int {
    case ILoveYou, Thanks
}

struct Message {
    let messageType: MessageType
}    
struct MessageILoveYou {
    let message: Message
    let messageSenderNumber: UInt32    
}

struct MessageThanks {
    let message: Message
    let messageSenderName: String
    let messageSenderNumber: UInt32    
}

func sendILoveYou() {    
    println("sendILoveYou:")
    let nameNumber = UInt32(56)
    var message = MessageILoveYou(message: Message(messageType: MessageType.ILoveYou), messageSenderNumber: nameNumber)
    let data = NSData(bytes: &message, length: sizeof(MessageILoveYou))
    sendData(data)    
}

func sendThanks() {
    println("sendThanks:")
    let nameString = "Don J"
    let senderNumberInt = UInt32(88)
    var message = MessageThanks(message: Message(messageType: MessageType.Thanks), messageSenderName: nameString, messageSenderNumber: senderNumberInt)        
    let data = NSData(bytes: &message, length: sizeof(MessageThanks))
    sendData(data)        
}

func matchReceivedData(match: GKMatch, data: NSData, fromPlayer player: String) {

    println("matchReceivedData:")
    var message = UnsafePointer<Message>(data.bytes).memory        
    if message.messageType == MessageType.ILoveYou {            
        println("messageType == ILoveYou")
        let messageILoveYou = UnsafePointer<MessageILoveYou>(data.bytes).memory
        iLoveYouThanksDelegate?.iLoveYouReceived(from: messageILoveYou.messageSenderNumber)

    } else if message.messageType == MessageType.Thanks {            
        println("messageType == Thanks")
        var messageThanks = UnsafePointer<MessageThanks>(data.bytes).memory
        iLoveYouThanksDelegate?.thanksReceived(from: messageThanks.messageSenderName)
    }
}

func sendData(data: NSData) {
    var sendDataError: NSError?
    let gameKitHelper = GameKitHelper.sharedInstance

    if let multiplayerMatch = gameKitHelper.multiplayerMatch {
        let success = multiplayerMatch.sendDataToAllPlayers(data, withDataMode: .Reliable, error: &sendDataError)
        if !success {
            if let error = sendDataError {
                println("Error:\(error.localizedDescription)")
                matchEnded()
            }
        }
    }
}

The problem here is that when you create a String in Swift, it allocates a bit of memory itself, and then uses that memory to store the actual characters of the string. 这里的问题是,当您在Swift中创建一个String时,它本身会分配一点内存,然后使用该内存来存储字符串的实际字符。 All that the string value really holds is some data representing a pointer to that memory and some other info (like how much memory has been allocated, so that it can be freed properly. 字符串值真正保留的只是代表该内存的指针的一些数据和其他一些信息(例如已分配了多少内存,以便可以正确释放它)。

You can see this here: 您可以在这里看到:

let str = "This is quite a long string, certainly more than 24 bytes"
sizeofValue(str)  // and yet this only returns 24

When you stuff variables into an NSData object, the initializer takes a pointer to the memory of the string variable that is holding those pointers, not the characters itself: 当您将变量填充到NSData对象中时,初始化程序将使用一个指针,该指针指向保存这些指针的字符串变量的内存, 而不是字符本身:

// only storing those 24 bytes, not the actual string
let data = NSData(bytes: &str, length: sizeofValue(str))

Note, the type of the bytes argument is UnsafePointer<Void> . 注意, bytes参数的类型是UnsafePointer<Void> This is an indication that you are heading into tricky territory. 这表明您正在进入棘手的领域。

Then, when you unmarshal the data at the other end, all your receiver is going to get is some pointers to random memory (sadly, memory on the other user's device!) 然后,当您在另一端解组数据时,您的接收器将获得的只是指向随机存储器的指针(可悲的是,另一用户设备上的存储器!)。

If you want to put string values into an NSData object, you are going to need to marshal them first into raw data. 如果要将字符串值放入NSData对象,则需要首先将它们编组为原始数据。 For example, you could encode them into an array: 例如,您可以将它们编码为一个数组:

let data = Array(str.utf8).withUnsafeBufferPointer { buf in
    NSData(bytes: buf.baseAddress, length: buf.count)
}

As it happens, since this is a common thing to want to do, there's a method to do this directly: 碰巧的是,由于这是常见的事情,因此有一种直接执行此操作的方法:

let data = str.dataUsingEncoding(NSUTF8StringEncoding)

Then, to unpack the data, you can use NSString 's constructor from an NSData object: 然后,要解压缩数据,可以从NSData对象使用NSString的构造函数:

let newStr = NSString(data: data, encoding: NSUTF8StringEncoding)

edit: if you wanted to encode more than just a string in a single NSData , you could do something along these lines… I should say, I've never had to do this myself so I'm in no way familiar with the standard practices for this, there could be much better techniques or helper classes/functions. 编辑:如果您想在单个NSData编码多个字符串,则可以按照以下方式做一些事情……我应该说,我从来没有自己做过这个事情,所以我对标准做法一点都不熟悉为此,可能会有更好的技术或助手类/功能。 Hopefully someone with more experience can edit to show how to do this properly :) 希望有更多经验的人可以编辑以显示如何正确执行此操作:)

var type = MessageType.Thanks

// start the data with the type
let data = NSMutableData(bytes: &type, length: sizeofValue(type))

// then append the string
data.appendData(Array(str.utf8).withUnsafeBufferPointer { buf in
    NSMutableData(bytes: buf.baseAddress, length: buf.count)
    })

switch UnsafePointer<MessageType>(data.bytes).memory {
case .ILoveYou:
// ...
case .Thanks:
    let str = NSString(data: data.subdataWithRange(NSMakeRange(1, data.length-1)), encoding: NSUTF8StringEncoding)
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM