当前位置 : 主页 > 手机开发 > 其它 >

Swift中的相互非可选引用循环

来源:互联网 收集:自由互联 发布时间:2021-06-11
考虑以下用例: 在某个游戏的模型中,你有一个Player类.每个玩家都有一个无主的让对手:玩家代表他们正在对抗的对手.这些总是成对创建,并且玩家必须始终拥有对手,因为它是非可选的
考虑以下用例:

在某个游戏的模型中,你有一个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并没有真正相同.更重要的是,它可能是一个有缺陷的方法开始.

就Swift而言,初始化器需要在返回之前初始化所有存储的值.出于多种原因,我不打算这样做.当初始化时无法保证/计算值时,将使用选项,IUO和计算值.如果您不想要Optionals,IUO或计算值,但仍希望在初始化后取消设置一些存储的值,那么您就想要吃蛋糕并吃掉它.

就设计而言,如果您需要将两个对象链接得如此紧密以至于在初始化时需要彼此,则您的模型(IMO)会被破坏.这是分层数据结构解决得很好的确切问题.在你的具体例子中,似乎很清楚你需要某种Match或Competition对象来创建和管理两个玩家之间的关系,我知道你的问题更接近于“这是否可能”而不是“应该完成”,但我想不出任何不是坏主意的情况.从根本上说它打破了封装.

Player对象应该管理和跟踪Player对象中存在的东西,而Player类中唯一的托管关系应该是它的子对象.任何兄弟关系都应该由它的父母访问/设置.

这成为一个更清晰的规模问题.如果你想添加第三个玩家怎么办? 50岁左右?然后,您必须初始化并将每个玩家连接到其他玩家,然后才能使用任何玩家.如果您想要添加或删除播放器,您必须同时为每个连接的播放器执行此操作,并阻止发生任何事情.

另一个问题是它在任何其他情况下都无法使用.如果设计得当,玩家可以在所有类型的游戏中使用.而目前的设计允许它仅在1v1情况下使用.对于任何其他情况,您将不得不重新编写它,您的代码库将会分歧.

总而言之,你想要的东西在Swift中可能是不可能的,但是如果它成为可能的话,反正几乎肯定是一个坏主意:)

对不起文章,希望你觉得它有用!

网友评论