考虑以下用例:
在某些游戏的模型中,您有一个Player
类。每个Player
人都有一个unowned let opponent: Player
代表他们正在对抗的对手。这些总是成对创建的,并且 aPlayer
必须总是有 aopponent
因为它是非可选的。但是,这很难建模,因为必须先创建一个玩家,而创建第二个玩家之前,第一个玩家不会有对手!
通过一些丑陋的黑客攻击,我想出了这个解决方案:
class Player {
private static let placeholder: Player = Player(opponent: .placeholder, name: "")
private init(opponent: Player, name: String) {
self.opponent = opponent
self.name = name
}
unowned var opponent: Player
let name: String
class func getPair(named names: (String, String)) -> (Player, Player) {
let p1 = Player(opponent: .placeholder, name: names.0)
let p2 = Player(opponent: p1, name: names.1)
p1.opponent = p2
return (p1, p2)
}
}
let pair = Player.getPair(named:("P1", "P2"))
print(pair.0.opponent.name)
print(pair.1.opponent.name)
效果很好。但是,我很难opponent
变成一个常数。一种解决方案是创建opponent
一个不带 a 的计算属性set
,由 a 支持var
,但我想避免这种情况。
我试图用 Swift 指针做一些黑客攻击,并想出了:
class func getPair(named names: (String, String)) -> (Player, Player) {
var p1 = Player(opponent: .placeholder, name: names.0 + "FAKE")
let p2 = Player(opponent: p1, name: names.1)
withUnsafeMutablePointer(to: &p1) {
var trueP1 = Player(opponent: p2, name: names.0)
$0.moveAssign(from: &trueP1, count: 1)
}
return (p1, p2)
}
但这给出了一个段错误。此外,当使用 进行调试时lldb
,我们可以看到在p1
初始化之后,我们有:
(lldb) p p1
(Player2.Player) $R3 = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
但在函数结束时,lldb 显示:
(lldb) p p1
(Player2.Player) $R5 = 0x00000001010062d0 {
opponent = 0x00000001010062a0 {
opponent = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
name = "P2"
}
name = "P1"
}
(lldb) p p2
(Player2.Player) $R4 = 0x00000001010062a0 {
opponent = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
name = "P2"
}
所以p1
正确地指向p2
,但p2
仍然指向旧的p1
。更何况,p1
居然换了地址!
我的问题有两个:
是否有一种更清洁、更“快捷”的方式来创建这种相互非可选引用的结构?
如果不是,我对
UnsafeMutablePointer
Swift 中的 s 等有什么误解,导致上述代码不起作用?