unity中的InstanceID详解 即Object.GetInstanceID

GetInstanceID 是 Unity 中 Object 类的一个方法,它用于获取一个对象的唯一实例标识符。每个 Unity 对象(如游戏对象、组件、资源等)都有一个唯一的实例 ID,这个 ID 在对象的生命周期内是唯一的。

对于它的生命周期是不确定的。网上说在切换场景或者编辑器关闭重启后会变。这些说法太片面,或者说根本不对。

下面做说明

首先在编辑器里在属性面板里点击右上角三个小点点切换到debug模式

然后随便选择一个资产(脚本也是资产的一种),将会看到InstanceId

        这个资产InstanceId是在编辑器启动时为每个资产分配的Id,他的生命周期是和编辑器生命周期一样的。即只有在关闭编辑器然后重启后才会重新分配发生变化,换句话说,只要编辑器不关闭,这个资产InstanceID就不会变。这个你可以重启编辑器测一下就知道。
        所以切换场景会发生变化这是错误的。但是切换场景会影响实例化的InstanceID而不是上面资产的InstanceId。后面会详细说明。

  • 加载资源:

    • 当你使用 AssetDatabase.LoadAssetAtPathResources.Load 加载资源时,你获取的是原始资产的引用。这个原始资产在编辑器中有一个唯一的实例InstanceID,这个InstanceID 是在原始资产创建时分配的。加载资产已分配的InstanceID,说白了加载时只是从资源管理器对应的文件里拿去资源数据包括InstanceID
    • 如下面代码
    •         // 加载一个资源对象,即原始资源
              GameObject prefab = Resources.Load<GameObject>("MyPrefab");

      此时的prefab就是资源管理器的资源文件的原始资源,所以在编辑器运行的生命周期里一直不会变。类似的还有AssetDatabase.LoadAssetAtPath也是一样的。此时的originPrefab的InstanceId和编辑器的时一摸一样的,不会变,因为此时加载的就是编辑器的资源数据。除非你删除资源,删除资源InstanceId会被回收,有人说掉用destroy方法销毁怎么样,呵呵,那是不行的直接报错。还有另一个销毁方法DestroyImmediate(originPrefab );也是不行,除非传入true,DestroyImmediate(originPrefab ,ture),但是传入true就删除资源一样的操作

  • 实例 InstanceId 的稳定性:

    • 在编辑器的生命周期内,原始资产的实例InstanceId是稳定的,即使你在编辑器中切换场景或进行其他操作,原始资产的实例 InstanceId 也不会变化。只有在编辑器关闭或重新启动时,原始资产的实例 InstanceId才可能被重新分配。
  • 删除原始资产:

    • 如果你从 Assets 文件夹中删除一个原始资产文件,那么它的实例 InstanceId 也会被回收,因为这个资产已经不再存在。此时,原始资产的引用会失效,不能再通过加载方法访问该资产。
  • 资产的原始资源是不会变的。但是但资产通过Object.Instance或者继承自Object间接调用Instance方法的实例化对象会分配新的InstanceId,当然Instance方法很多情况传入的时原始资源。如下面代码
  •         //加载一个资源对象,即原始资源,此时的originPrefab的InstanceId和编辑器的时一摸一样的,不会变,除非你删除资源,删除资源InstanceId会被回收,有人说掉用destroy方法销毁怎么样,呵呵,那是不行的直接报错。还有另一个销毁方法DestroyImmediate(originPrefab );也是不行,除非传入true,DestroyImmediate(originPrefab ,ture),但是传入true就删除资源一样的操作
            GameObject originPrefab = Resources.Load<GameObject>("MyPrefab");
            GameObject entity=Object.Instance(originPrefab );

    这种操作originPrefab也是不会变的,也为他就是原始资源,通过 Object.Instance获取新对象entity时,方法会为entity分配新的InstanceId,不会改变originPrefab的InstanceId,在每次Object.Instance都会分配一个新的,但是如果entity被销毁后InstanceId会被回收,如果在回收后再Object.Instance获得entity可能会和原来的对象InstanceId是同一个。

  • 这时候,如果切换的场景所有的实例化后的对象(注意不是加载的原始资产)都会被销毁,InstanceId会被回收。然后在重新实例化后,InstanceId会重新分配,造成实例化后的对象InstanceId变化。就是说同一个原始资产实例化化后的对象InstanceId是会变的。但是原始资产不会变。

  • 总结

  • 通过AssetDatabase.LoadAssetAtPathResources.Load 加载获得的对象就是资源管理器文件对象的InstanceId,因为对象是一个东西。

  • 只有通过Object.Instance或者继承自Object间接调用Instance方法的实例化对象会分配新的InstanceId,获得不同的InstanceId,资产原始文件对象是不会改变影响的。

  • InstanceId是很有用的。AssetDatabase.LoadAssetAtPathResources.Load加载的资源文件对象会加载到内存里不会被销毁。基础如图片资源加载,可以在获取原始资源后调用

    Resources.UnloadAsset(Object)传入原始对象资源,是可以销毁内存资源的,不能对原始资源使用Destoy方法。但是对于prefab资源加载是致命的,因为prefab会依赖其他资源,在加载是unity会默认加载这些依赖的资源。我们是无法获取 那些资源的。也是最严重的就是prefab,在unity中,原始资源prefab是无法通过Resources.UnloadAsset回收的,会报错。调用
    Resources.UnloadUnusedAssets()也是没效果的,给资源调试带来不可能检测的问题。这就是为什么必须真机调试的原因了。因为无法卸载。
  • 但是InstanceId给我们在编辑器模式下卸载内存资源,编辑器模式调试资源带来可能。首先通过AssetDatabase.GetDependencies(path, true)获取加载prefab的所有加载的依赖资源,记录所有依赖资源的InstanceId,通过Resources.FindObjectsOfTypeAll();获取指定类型的内存中原始资源对象。循环遍历通过InstanceId匹配内存对象。然后获取内存对象后,通过Resources.UnloadAsset(Object)卸载内存。但是prefab是无法卸载的,prefab几乎不占内存,说白了prefab就是组织资源的一个配置文件。只要把相关依赖的基础资源卸载,prefab占的资源可以忽略。因为在下次加载prefab原始资源是加载返回的还是这个prefab内存资源,只不过重新加载依赖的资源。即使重新加载prefab这个配置文件也会影响多大。但是在资源中图集还是要单独处理。当然可以使用资源打包能完全做到内存卸载,但是开发阶段,不可能通过打包做资源测试。这个成本巨大,在开发阶段还是要通过AssetDatabase.LoadAssetAtPathResources.Load做资源加载。
  • 后续有时间会写资源编辑器资源脚本。通过AssetDatabase.LoadAssetAtPathResources.Load做资源加载。使在编辑器资源调试成为 可能,敬请期待吧。应该也是全网独份。因为截至现在,还没有人做到。即使官网Addressables也无法做到卸载,真是硬伤。。。

  • 16
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值