在某个游戏的模型中,你有一个Player类.每个玩家都有一个无主的让对手:玩家代表他们正在对抗的对手.这些总是成对创建,并且玩家必须始终拥有对手,因为它是非可选的.然而,这很难建模,因为一个玩家必须先创建另一个玩家,并且第一个玩家在创建第二个玩家之前不会有对手!
通过一些丑陋的黑客攻击,我想出了这个解决方案:
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)
哪个效果很好.但是,我无法将对手变成常数.一种解决方案是让对手成为没有集合的计算属性,由私有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之类的误解是什么使得上面的代码不起作用?
就Swift而言,初始化器需要在返回之前初始化所有存储的值.出于多种原因,我不打算这样做.当初始化时无法保证/计算值时,将使用选项,IUO和计算值.如果您不想要Optionals,IUO或计算值,但仍希望在初始化后取消设置一些存储的值,那么您就想要吃蛋糕并吃掉它.
就设计而言,如果您需要将两个对象链接得如此紧密以至于在初始化时需要彼此,则您的模型(IMO)会被破坏.这是分层数据结构解决得很好的确切问题.在你的具体例子中,似乎很清楚你需要某种Match或Competition对象来创建和管理两个玩家之间的关系,我知道你的问题更接近于“这是否可能”而不是“应该完成”,但我想不出任何不是坏主意的情况.从根本上说它打破了封装.
Player对象应该管理和跟踪Player对象中存在的东西,而Player类中唯一的托管关系应该是它的子对象.任何兄弟关系都应该由它的父母访问/设置.
这成为一个更清晰的规模问题.如果你想添加第三个玩家怎么办? 50岁左右?然后,您必须初始化并将每个玩家连接到其他玩家,然后才能使用任何玩家.如果您想要添加或删除播放器,您必须同时为每个连接的播放器执行此操作,并阻止发生任何事情.
另一个问题是它在任何其他情况下都无法使用.如果设计得当,玩家可以在所有类型的游戏中使用.而目前的设计允许它仅在1v1情况下使用.对于任何其他情况,您将不得不重新编写它,您的代码库将会分歧.
总而言之,你想要的东西在Swift中可能是不可能的,但是如果它成为可能的话,反正几乎肯定是一个坏主意:)
对不起文章,希望你觉得它有用!