我正在尝试创建一个多人游戏,将发送移动之间的球员使用游戏中心。我仍然在学习编程,所以如果我的问题是错误的,请原谅我。另外,我对obj-c不太熟悉,所以快速的回答会很好。
在我的玩具程序中,我尝试着按照shayne meyer在gamekitshelper类中使用的策略来学习:https://github.com/shaynemeyer/SwiftCircuitRacer/tree/master/SwiftCircuitRacer
使用这种方法,shayne使用作为nsdata发送的struct在线向其他玩家发送消息。我可以发送整数(例如,iloveyou消息),但不能发送带有字符串属性的消息(例如,感谢消息)。在后一种情况下,我在“var messageshenks=unsafepointer,messsageshenks>(data.bytes.memory)”行得到“thread 1:exc_bad_access(code=1,address=0x78674100)”。
最后,我想发送游戏动作,提供字符串和整数在一起。当属性也包含字符串时,如何将消息结构作为nsdata发送?其次,如果有人能帮助我从根本上了解数据打包时的情况,以及unsafepointer在通过游戏中心发送数据时的工作方式,我将不胜感激。
谢谢您。
悬崖
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()
}
}
}
}
最佳答案
这里的问题是,当您在swift中创建字符串时,它会自己分配一点内存,然后使用该内存来存储字符串的实际字符。字符串值真正保存的只是一些表示指向该内存的指针的数据和一些其他信息(比如已经分配了多少内存,以便可以正确释放内存)。
你可以在这里看到:
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)
}