【发布时间】:2016-10-13 17:05:51
【问题描述】:
考虑以下用例:
在某些游戏的模型中,您有一个 Player 类。每个Player 都有一个unowned let opponent: Player,代表他们正在对抗的对手。这些总是成对创建的,Player 必须始终有一个opponent,因为它是非可选的。但是,这很难建模,因为必须先创建一个玩家,而创建第二个玩家之前,第一个玩家将没有对手!
通过一些丑陋的黑客攻击,我想出了这个解决方案:
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 成为没有 set 的计算属性,由私有 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居然换了地址!
我的问题有两个:
是否有一种更简洁、更“快捷”的方式来创建这种相互非可选引用的结构?
如果不是,我对 Swift 中的
UnsafeMutablePointers 等有什么误解,导致上述代码不起作用?
【问题讨论】:
-
我认为你的模型是错误的。你有一个
Match,它必须有2个Players;并且每个Player对它们所属的Match都有一个弱引用。在该模型中,您可以将opponent设为计算属性 -
当然,模型可以重构;我很想知道 Swift 是否可以支持这种模型。更一般地,您可以想象“外部初始化”的概念,其中可以在未初始化状态下创建对象,然后在外部设置其属性,并且在正确初始化所有属性之前无法返回或使用(就像 @987654347 @ 现在行为)。我知道 Swift 在如此高的级别上不支持此功能,但我很想知道该行为是否至少可以被模拟。更改模型使其成为一个不那么有趣的问题。
-
特别是,该模型失败了,因为
Players 突然不知道他们是哪个Player——opponent必须手动检查是self == match.p1还是self == match.p2。我想你可以在Match上创建一个对手方法,这样你就可以做var opponent { return match.opponent(self) },但我仍然认为Players 在概念上不是一个不正确的模型,每个人都知道他们在给定的@中面对的是谁987654356@.
标签: swift macos unsafemutablepointer