13.Unity Zenject高级编程(MemoryPools内存池)


示例

使用Unity开发游戏使用适当的内存管理非常重要,如果你想制作流畅的游戏在手机上面运行;
根据运行的平台和制作的游戏类型不同,对于尽可能的避免开辟不需要的堆内存非常的重要;
最有效的途径的使用内存池,代码示例

public class Foo
{
   
    public class Factory : PlaceholderFactory<Foo>
    {
   
    }
}

public class Bar
{
   
    readonly Foo.Factory _fooFactory;
    readonly List<Foo> _foos = new List<Foo>();

    public Bar(Foo.Factory fooFactory)
    {
   
        _fooFactory = fooFactory;
    }

    public void AddFoo()
    {
   
        _foos.Add(_fooFactory.Create());
    }

    public void RemoveFoo()
    {
   
        _foos.RemoveAt(0);
    }
}

public class TestInstaller : MonoInstaller<TestInstaller>
{
   
    public override void InstallBindings()
    {
   
        Container.Bind<Bar>().AsSingle();
        Container.BindFactory<Foo, Foo.Factory>();
    }
}

这里,每次我们调用Bar.AddFoo将会开辟新的堆内存;每次我们调用Bar.RemoveFoo,Bar类将会从Foo的实例里面停止引用,因此Foo的实例内存标记为可以垃圾回收;如果这样发生的次数过多,最终垃圾回收器将变成负担并且你的游戏由于
spike(急剧上升)过高导致卡顿,使用memory pools来修复急剧上升的开销替代

public class Foo
{
   
    public class Pool : MemoryPool<Foo>
    {
   
    }
}

public class Bar
{
   
    readonly Foo.Pool _fooPool;
    readonly List<Foo> _foos = new List<Foo>();

    public Bar(Foo.Pool fooPool)
    {
   
        _fooPool = fooPool;
    }

    public void AddFoo()
    {
   
        _foos.Add(_fooPool.Spawn());
    }

    public void RemoveFoo()
    {
   
        var foo = _foos[0];
        _fooPool.Despawn(foo);
        _foos.Remove(foo);
    }
}

public class TestInstaller : MonoInstaller<TestInstaller>
{
   
    public override void InstallBindings()
    {
   
        Container.Bind<Bar>().AsSingle();
        Container.BindMemoryPool<Foo, Foo.Pool>();
    }
}

正如你看到的这样,内存池和工厂非常相似,除了部分的术语一点不同之后,
不像工厂你需要返回实例给池子相比工厂的仅仅是停止引用实例
使用了这种新的实现方式,将会在调用AddFoo()初始化的时候分配堆内存,
但你调用RemoveFoo()然后再AddFoo(),池子序列将重用之前的实例并因此节省你的堆内存开销
这样更好,我们想避免因为初始化导致内存的急剧上升;解决方法是再游戏启动的时候初始化好所有的内存开销

public class TestInstaller : MonoInstaller<TestInstaller>
{
   
    public override void InstallBindings()
    {
   
        Container.Bind<Bar>().AsSingle();
        Container.BindMemoryPool<Foo, Foo.Pool>().WithInitialSize(10);
    }
}

当我们使用WithInitialSize语句绑定语法到我的池子,10个实例化的Foo将会在启动的时候立即创建作为池子的种子

Binding Syntax 绑定语法

内存池的语法大多数和工厂一样,只有部分新的绑定方法,类如 WithInitialSize 和 ExpandBy;同样,不像BindFactory,
没有必要给工厂指定参数,BindMemoryPool生成参数
和工厂一样,推荐使用内部公开类命名为Pool

public class Foo
{
   
    public class Pool : MemoryPool<Foo>
    {
   
    }
}

通过添加生成参数来设置参数

public class Foo
{
   
    public class Pool : MemoryPool<string, int, Foo>
    {
   
    }
}

全部的绑定语法格式

Container.BindMemoryPool<ObjectType, MemoryPoolType>()
    .With(InitialSize|FixedSize)
    .WithMaxSize(MaxSize)
    .ExpandBy(OneAtATime|Doubling)()
    .WithFactoryArguments(Factory Arguments)
    .To<ResultType>()
    .WithId(Identifier)
    .FromConstructionMethod()
    .AsScope()
    .WithArguments(Arguments)
    .OnInstantiated(InstantiatedCallback)
    .When(Condition)
    .CopyIntoAllSubContainers()
    .NonLazy();

字段说明:

  • InitialSize - 这个值决定启动的时候播种多少个对象到内存池里面;对于避免游戏期间急剧上升的内存有帮助
  • FixedSize - 设置这个值,内存池将会初始化指定的数量种子,如果这个值超过了,则抛异常
  • MaxSize - 设置这个值,如果有足够的对象已经返回到池子里面,超过了内存池的最大数量,多余的对象将会被销毁;
    这个决定内存池最大开辟的内存空间
  • ObjectType - 通过内存池实例化的类类型
  • MemoryPoolType - MemoryPool派生类的类型,通常是一个内部类命名为Pool
  • ExpandBy - 当内存池达到最大数量的时候决定内存池的怎么进行拓展,注意当使用WithFixedSize的时候,这个选项无效
  1. ExpandByOneAtATime - 当有需求的时候,一次只多开辟一个新对象
  2. ExpandByDoubling - 当内存池满了并且有一个新实例请求,内存池将开辟双倍的容量在返回请求实例之前;
    这个方法在需要开辟数量多,功能小的对象非常有帮助
  • WithFactoryArguments - 如果你想注入额外的参数到内存池的派生类,你可以通过这里传递,
    注意WithArguments是给实例传递参数而不是内存池传递参数
  • Scope - 注意和正常的绑定不一样,默认是AsCached替代AsTransient
    剩下的绑定方法和正常的绑定方法一样
Resetting Items In Pool 重置池子对象

我们要注意使用内存池替代工厂必须确保完全重置了指定的实例;这个是非常有必要的,
有可能上一个状态来之之前的数 据,重新实例化之后还保留了原来的数据,内存池的实现方法有下列这些

public class Foo
{
   
    public class Pool : MemoryPool<Foo>
    {
   
        protected override void OnCreated(Foo item)
        {
   
            // Called immediately after the item is first added to the pool
        }

        protected override void OnDestroyed(Foo item)
        {
   
            // Called immediately after the item is removed from the pool without also being spawned
            // This occurs when the pool is shrunk either by using WithMaxSize or by explicitly shrinking the pool by calling the `ShrinkBy` / `Resize methods
        }

        protected override void OnSpawned(Foo item)
        {
   
            // Called immediately after the item is removed from the pool
        }

        protected override void OnDespawned(Foo item)
        {
   
            // Called immediately after the item is returned to the pool
        }

        protected override void Reinitialize(Foo foo)
        {
   
            // Similar to OnSpawned
            // Called immediately after the item is removed from the pool
            // This method will also contain any parameters that are passed along
            // to the memory pool from the spawning code
        }
    }
}

大多数情况下,你大概只要实现Reinitialize方法;代码示例

public class Foo
{
   
    Vector3 _position = Vector3.zero;

    public void Move(Vector3 delta)
    {
   
        _position += delta;
    }

    public class Pool : MemoryPool<Foo>
    {
   
        protected override void Reinitialize(Foo foo)
        {
   
            foo._position = Vector3.zero;
        }
    }
}

注意我们的池子可以自由访问Foo的私有变量,因为池子是一个内部类;或者,我们避免在Foo和Foo.Pool里面重复出现

public class Foo
{
   
    Vector3 _position;

    public Foo()
    {
   
        Reset();
    }

    public void Move(Vector3 delta)
    {
   
        _position += delta;
    }

    void Reset()
    {
   
        _position = Vector3.zero;
    }

    public class Pool : MemoryPool<Foo>
    {
   
        protected override void Reinitialize(Foo foo)
        {
   
            foo.Reset();
        }
    }
}
运行时传递参数

和工厂一样,你可以在运行时生成新的实例传递参数,不同的是,替代参数注入到类里面,通过Reinitialize方法

public class Foo
{
   
    Vector3 _position;
    Vector3 _velocity;

    public Foo()
    {
   
        Reset(Vector3.zero);
    }

    public void Tick()
    {
   
        _position += _velocity * Time.deltaTime;
    }

    void Reset(Vector3 velocity)
    {
   
        _position = Vector3.zero;
        _velocity = Vector3.zero;
    }

    public class Pool : MemoryPool<Vector3, Foo>
    {
   
        protected override void Reinitialize(Vector3 velocity, Foo foo)
        {
   
            foo.Reset(velocity);
        }
    }
}

public class Bar
{
   
    readonly Foo.Pool _fooPool
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值