RealityView中Skybox实体内存未释放问题求助
问题描述
在RealityView中按需添加Skybox(有时无需显示内容),但视图销毁后内存未释放,每次切换contentString时内存占用持续叠加。具体表现为:应用启动内存约500MB,在2张Skybox图片与nil间切换4-5轮后,内存升至1GB,即使关闭沉浸式空间也无法释放该内存。
相关代码如下:
var body: some View { RealityView { content in let rootEntity = Entity() rootEntity.addSkybox(for: contentString) content.add(rootEntity) } .transition(.opacity) } func addSkybox(for destination: String) { let subscription = TextureResource.loadAsync(named: destination).sink( receiveCompletion: { switch $0 { case .finished: break case .failure(let error): assertionFailure("\(error)") } }, receiveValue: { [weak self] texture in guard let self = self else { return } var material = UnlitMaterial() material.color = .init(texture: .init(texture)) self.components.set(ModelComponent( mesh: .generateSphere(radius: 1E3), materials: [material] )) self.scale *= .init(x: -1, y: 1, z: 1) self.transform.translation += SIMD3<Float>(0.0, 1.0, 0.0) // Rotate the sphere to show the best initial view of the space. updateRotation(for: destination) } ) components.set(Entity.SubscriptionComponent(subscription: subscription)) } // 每次切换到nil再切回其他内容,内存都会在这里堆积 @State var imageSkybox: String? = "Starfield" if let imageSkybox = imageSkybox { LandscapeImmersiveView(contentString: imageSkybox) .frame(depth: 0) }
解决方案
1. 手动清理RealityView中的实体资源
RealityView不会自动清理所有关联资源,需在视图销毁时手动移除实体并释放订阅、模型组件:
RealityView { content in let rootEntity = Entity() rootEntity.addSkybox(for: contentString) content.add(rootEntity) } onDisappear: { content in for entity in content.entities { // 取消异步加载订阅 if let subscriptionComp = entity.components[Entity.SubscriptionComponent.self] { subscriptionComp.subscription.cancel() } // 移除模型组件释放纹理和网格 entity.components.remove(ModelComponent.self) content.remove(entity) } } .transition(.opacity)
2. 复用Skybox实体而非每次重建
避免重复创建新实体,复用同一个实体并更新纹理即可:
@State private var skyboxEntity = Entity() var body: some View { RealityView { content in content.add(skyboxEntity) updateSkybox(for: contentString) } onDisappear: { _ in // 清理当前资源 skyboxEntity.components.remove(ModelComponent.self) if let subscriptionComp = skyboxEntity.components[Entity.SubscriptionComponent.self] { subscriptionComp.subscription.cancel() skyboxEntity.components.remove(Entity.SubscriptionComponent.self) } } .transition(.opacity) } func updateSkybox(for destination: String) { // 先取消之前的订阅 if let subscriptionComp = skyboxEntity.components[Entity.SubscriptionComponent.self] { subscriptionComp.subscription.cancel() skyboxEntity.components.remove(Entity.SubscriptionComponent.self) } let subscription = TextureResource.loadAsync(named: destination).sink( receiveCompletion: { switch $0 { case .finished: break case .failure(let error): assertionFailure("\(error)") } }, receiveValue: { [weak self] texture in guard let self = self else { return } var material = UnlitMaterial() material.color = .init(texture: .init(texture)) self.skyboxEntity.components.set(ModelComponent( mesh: .generateSphere(radius: 1E3), materials: [material] )) self.skyboxEntity.scale *= .init(x: -1, y: 1, z: 1) self.skyboxEntity.transform.translation += SIMD3<Float>(0.0, 1.0, 0.0) updateRotation(for: destination) } ) skyboxEntity.components.set(Entity.SubscriptionComponent(subscription: subscription)) }
3. 手动清理纹理缓存
TextureResource可能被系统缓存,在视图销毁后可尝试清除缓存(注意:此方法会清除所有纹理缓存,需谨慎使用):
// 在onDisappear回调中添加 TextureResource.clearCache()
4. 检查旋转逻辑的内存泄漏
确保updateRotation方法中没有强引用导致的泄漏,比如使用[weak self]捕获列表:
func updateRotation(for destination: String) { DispatchQueue.main.async { [weak self] in guard let self = self else { return } // 执行旋转逻辑 } }
内容的提问来源于stack exchange,提问作者Zack Routin
相关产品推荐
相关产品推荐

