我正在尝试创建一个多人游戏,使用Game Center在玩家之间发送动作。我还在学习很多关于编程的知识,所以如果我的问题不正确,请原谅。另外,我对Obj-C不太熟悉,所以Swift的答案会很棒。
在我尝试自学的玩具程序中,我试图遵循Shayne Meyer使用GameKitHelper类所使用的策略:https://github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer
使用这种方法,Shayne使用作为NSData发送的结构在线向其他玩家发送消息。我能够发送整数(例如,ILoveYou消息)但不能发送带有字符串属性的消息(例如,Thanks消息)。在后一种情况下,我在“var messageThanks = UnsafePointer,MesssageThanks>(data.bytes).memory”
这一行得到“Thread 1:EXC_BAD_ACCESS(code = 1,address = 0x78674100)”最终,我想发送同时提供字符串和整数的游戏动作。当属性还包含字符串时,如何将消息结构作为NSData发送?其次,如果有人能够帮助我从根本上了解数据打包时的情况以及UnsafePointer正在做什么,因为它与通过Game Center发送数据有关,我将不胜感激。
谢谢。 崖
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()
}
}
}
}
答案 0 :(得分:0)
这里的问题是,当你在Swift中创建一个String时,它会自己分配一些内存,然后使用该内存来存储字符串的实际字符。字符串值实际上保存的是一些数据,表示指向该内存的指针和一些其他信息(例如已分配了多少内存,以便可以正确释放它。
你可以在这里看到:
let str = "This is quite a long string, certainly more than 24 bytes"
sizeofValue(str) // and yet this only returns 24
当你将变量填充到NSData
对象中时,初始化器会获取一个指针,该指针指向保存这些指针的字符串变量的内存,不字符本身:
// only storing those 24 bytes, not the actual string
let data = NSData(bytes: &str, length: sizeofValue(str))
注意,bytes
参数的类型为UnsafePointer<Void>
。这表明你正在进入棘手的领域。
然后,当您在另一端解组数据时,您的所有接收器都将获得随机存储器的一些指示(遗憾的是,其他用户设备上的内存!)
如果要将字符串值放入NSData
对象,则需要先将它们编组为原始数据。例如,您可以将它们编码为数组:
let data = Array(str.utf8).withUnsafeBufferPointer { buf in
NSData(bytes: buf.baseAddress, length: buf.count)
}
碰巧,因为这是一个常见的事情,有一种方法可以直接这样做:
let data = str.dataUsingEncoding(NSUTF8StringEncoding)
然后,要解压缩数据,可以使用NSString
对象中的NSData
构造函数:
let newStr = NSString(data: data, encoding: NSUTF8StringEncoding)
编辑:如果你想在一个NSData
中编码不仅仅是一个字符串,你可以沿着这些方向做点什么...我应该说,我自己从来没有这样做过,所以我在不熟悉这方面的标准做法,可能有更好的技术或辅助类/功能。希望有经验的人可以编辑以显示如何正确地执行此操作:)
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)
}