You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何解决GameKit中GKAgent、GKBehaviour与组件间的循环引用及内存泄漏?

GameKit GKAgent/GKBehaviour 循环引用问题解决指南

嘿,我碰到过类似的GameKit内存泄漏坑,来给你拆解下问题和解决方案~

首先,咱们先搞清楚循环引用的根源:GKAgent2D 会强引用它的 GKBehaviour,而 GKBehaviour 里的 GKGoal(尤其是那些依赖其他Agent/Component的目标)如果又强引用回Agent或者你的DroneMoveComponent,就会形成双向强引用,导致两者都无法被ARC回收,进而引发内存泄漏。

先看你给出的droneEntities()方法,这里有个潜在风险:你直接返回了[GKAgent2D]强引用数组,如果外部代码长期持有这个数组,会间接延长所有Agent和关联Component的生命周期,加重泄漏问题。

接下来是具体的解决步骤:

1. 用弱引用打破核心循环

如果你的自定义GKGoal需要引用DroneMoveComponent或者关联的GKEntity,一定要用弱引用声明:

// 自定义Goal示例,弱引用目标Component
class CustomSeekGoal: GKGoal {
    weak var targetComponent: DroneMoveComponent?
    
    init(targetComponent: DroneMoveComponent) {
        self.targetComponent = targetComponent
        super.init()
        // 这里实现你的目标逻辑
    }
}

另外,对于系统自带的GKGoal(比如toSeekAgent:),苹果文档标注部分Goal内部用的是弱引用,但保险起见,如果你发现某个Goal导致泄漏,可以自己封装一层弱引用逻辑。

2. 在Component销毁时主动断开Behaviour引用

在你的DroneMoveComponentdeinit方法里,主动将Agent的behaviour设为nil,强制打破Agent和Behaviour之间的强引用链:

class DroneMoveComponent: GKAgent2D {
    // ... 你的其他代码
    
    deinit {
        // 断开强引用,让Behaviour能被回收
        self.behaviour = nil
        print("DroneMoveComponent deinit成功") // 可以加这句验证是否泄漏
    }
}

这一步非常关键,即使你用了弱引用,主动清理也能确保引用链被及时断开。

3. 优化droneEntities()方法,避免不必要的强持有

把返回的强引用数组改成弱引用容器数组,这样外部代码持有这个数组不会导致Agent无法被回收:

// 先定义一个通用的弱引用容器
class Weak<T: AnyObject> {
    weak var value: T?
    init(value: T) {
        self.value = value
    }
}

// 修改你的方法
func droneEntities() -> [Weak<GKAgent2D>] { 
    for componentSystem in componentSystems { 
        if componentSystem.componentClass is DroneMoveComponent.Type { 
            return componentSystem.components.compactMap { component in 
                guard let agent = component as? GKAgent2D else { return nil }
                return Weak(value: agent)
            } 
        } 
    }
    return [] 
}

使用的时候,只需要取出value属性并判断是否为nil即可:

for weakAgent in droneEntities() {
    guard let agent = weakAgent.value else { continue }
    // 处理agent逻辑
}

4. 检查Entity与Component的引用关系

如果你的DroneMoveComponent需要引用它所属的GKEntity,一定要把这个引用声明为弱引用:

class DroneMoveComponent: GKAgent2D {
    weak var entity: GKEntity? // 弱引用,避免Entity和Component循环引用
    // ...
}

验证泄漏情况

你可以用Xcode的Memory Graph Debugger来检查:运行游戏,操作触发实体销毁,然后查看内存图里是否还有DroneMoveComponentGKAgent2DGKBehaviour的残留实例,如果没有,说明泄漏已经解决。

内容的提问来源于stack exchange,提问作者Jason

火山引擎 最新活动