Swift 捕获计算字符串时的引用循环 属性 从实例方法内部声明
Swift Reference Cycle When Capturing A Computed String Property Declared From Inside An Instance Method
我正在使用 Swift 和 SpriteKit 实现一个 in-game 商店。有一个名为 Store
的 class,它有一个方法 setupItems()
,我们在其中声明和实例化 class StoreItem
的实例,并添加每个商店项目实例作为 Store
的 child。每个 StoreItem
都有一个名为 updateInventory
的可选闭包 属性,它也在 setupItems()
内部设置。这是一个可选的关闭,因为一些项目没有有限的库存。
Store
还有一个unowned
实例属性storeDelegate
,它负责决定如何扣除资金以及如何在购买后应用storeItems
。 storeDelegate
是 unowned
,因为它的生命周期等于或大于 Store
。
现在,事情变得有趣了 - 闭包 updateInventory
引用了一个名为 itemText
的计算字符串变量,它根据 storeDelegate
的 属性 进行计算.如果 itemText
被声明并实例化为 setupItems()
内的变量,我们有一个引用循环并且存储不会被释放。相反,如果 itemText
被声明并实例化为 Store
的实例 属性(就在它引用的 属性 unowned storeDelegate
下方),则没有引用循环一切都在应该的时候解除分配。
这似乎暗示在 class 的实例方法中从计算变量引用 storeDelegate
不遵守 unowned
限定符。代码示例如下:
当前场景
protocol StoreDelegate: AnyObject {
func subtractFunds(byValue value: Int)
func addInventoryItem(item: InventoryItem) throws
var player: Player! { get }
}
class Store: SKSpriteNode, StoreItemDelegate {
unowned var storeDelegate: StoreDelegate
init(storeDelegate: StoreDelegate) {
self.storeDelegate = storeDelegate
setupItems()
...
}
func setupItems() {
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem = StoreItem(name: "coolItem")
storeItem.updateInventory = {
[unowned storeItem, unowned self] in
// some logic to check if the update is valid
guard self.storeDelegate.player.canUpdate() else {
return
}
storeItem.label.text = itemText
}
}
以上导致引用循环,有趣的是如果我们移动
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
在updateItems
之外,在unowned var storeDelegate: StoreDelegate
的正下方做成Store
的实例变量,就没有循环引用了。
我不知道为什么会这样,也没有在文档中看到它。如有任何建议,我们将不胜感激,如果您需要任何其他详细信息,请告诉我。
storeItem.updateInventory
现在保持对 itemText
.
的强引用
我认为问题在于 itemText
隐含地持有对 self
的强引用以访问 storeDelegate
,而不是保留对 storeDelegate
的引用。另一种选择是,即使 self
将委托保留为 unowned
,一旦您将 ot 传递给 itemText
进行保留,它就会被管理(即再次强引用)。
无论哪种方式,您都可以保证不保持强引用将 itemText
更改为函数并直接传递委托:
func setupItems() {
func itemText(with delegate: StoreDelegate) -> String
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem...
storeItem.updateInventory = {
// ...
storeItem.label.text = itemText(with self.storeDelegate)
}
}
我正在使用 Swift 和 SpriteKit 实现一个 in-game 商店。有一个名为 Store
的 class,它有一个方法 setupItems()
,我们在其中声明和实例化 class StoreItem
的实例,并添加每个商店项目实例作为 Store
的 child。每个 StoreItem
都有一个名为 updateInventory
的可选闭包 属性,它也在 setupItems()
内部设置。这是一个可选的关闭,因为一些项目没有有限的库存。
Store
还有一个unowned
实例属性storeDelegate
,它负责决定如何扣除资金以及如何在购买后应用storeItems
。 storeDelegate
是 unowned
,因为它的生命周期等于或大于 Store
。
现在,事情变得有趣了 - 闭包 updateInventory
引用了一个名为 itemText
的计算字符串变量,它根据 storeDelegate
的 属性 进行计算.如果 itemText
被声明并实例化为 setupItems()
内的变量,我们有一个引用循环并且存储不会被释放。相反,如果 itemText
被声明并实例化为 Store
的实例 属性(就在它引用的 属性 unowned storeDelegate
下方),则没有引用循环一切都在应该的时候解除分配。
这似乎暗示在 class 的实例方法中从计算变量引用 storeDelegate
不遵守 unowned
限定符。代码示例如下:
当前场景
protocol StoreDelegate: AnyObject {
func subtractFunds(byValue value: Int)
func addInventoryItem(item: InventoryItem) throws
var player: Player! { get }
}
class Store: SKSpriteNode, StoreItemDelegate {
unowned var storeDelegate: StoreDelegate
init(storeDelegate: StoreDelegate) {
self.storeDelegate = storeDelegate
setupItems()
...
}
func setupItems() {
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem = StoreItem(name: "coolItem")
storeItem.updateInventory = {
[unowned storeItem, unowned self] in
// some logic to check if the update is valid
guard self.storeDelegate.player.canUpdate() else {
return
}
storeItem.label.text = itemText
}
}
以上导致引用循环,有趣的是如果我们移动
var itemText: String {
return "item info goes here \(storeDelegate.player.health)"
}
在updateItems
之外,在unowned var storeDelegate: StoreDelegate
的正下方做成Store
的实例变量,就没有循环引用了。
我不知道为什么会这样,也没有在文档中看到它。如有任何建议,我们将不胜感激,如果您需要任何其他详细信息,请告诉我。
storeItem.updateInventory
现在保持对 itemText
.
我认为问题在于 itemText
隐含地持有对 self
的强引用以访问 storeDelegate
,而不是保留对 storeDelegate
的引用。另一种选择是,即使 self
将委托保留为 unowned
,一旦您将 ot 传递给 itemText
进行保留,它就会被管理(即再次强引用)。
无论哪种方式,您都可以保证不保持强引用将 itemText
更改为函数并直接传递委托:
func setupItems() {
func itemText(with delegate: StoreDelegate) -> String
return "item info goes here \(storeDelegate.player.health)"
}
let storeItem...
storeItem.updateInventory = {
// ...
storeItem.label.text = itemText(with self.storeDelegate)
}
}