You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 20:42:14