【前言】
查看NativeList源码需要安装Unity的Entities Package
NativeList要实现的基本功能类似C# List,如下:
(一些简单的类同NativeArray的不在说明)
构造函数、析构函数、取值赋值
扩容、添加、移除操作
解析步骤包括,基本函数,常用操作,常用属性,接口函数
【源码分析】
定义
//一个泛型结构体,且是unsafe的,继承了三个接口,并要求泛型是非托管类型的,如果T是引用类型的会报错
public unsafe struct NativeList<T> : INativeDisposable, INativeList<T>, IIndexable<T> where T : unmanaged
{
}
构造函数
public NativeList(AllocatorManager.AllocatorHandle allocator)
: this(1, allocator)
{
}
public NativeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator)
{
this = default;
AllocatorManager.AllocatorHandle temp = allocator;
Initialize(initialCapacity, ref temp);
}
internal void Initialize<U>(int initialCapacity, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
{
var totalSize = sizeof(T) * (long)initialCapacity; //sizeof用于计算值类型对象所占用的内存大小,与容量相乘,得到初始化需要的内存大小
m_ListData = UnsafeList<T>.Create(initialCapacity, ref allocator, NativeArrayOptions.UninitializedMemory);//listData是当前结构体的字段,调用UnsafeList的静态方法创建
}
//数据所在
[NativeDisableUnsafePtrRestriction]//该特性允许使用指针
internal UnsafeList<T>* m_ListData;//指向数据的指针
接着看UnsafeList
//同NativeList一样
public unsafe struct UnsafeList<T> : INativeDisposable, INativeList<T>, IIndexable<T> where T : unmanaged
{
}
直接看Create方法
internal static UnsafeList<T>* Create<U>(int initialCapacity, ref U allocator, NativeArrayOptions options) where U : unmanaged, AllocatorManager.IAllocator
{
UnsafeList<T>* listData = allocator.Allocate(default(UnsafeList<T>), 1);//通过Allocator分配内存,返回该内存起始地址的指针 NativeArrayOptions有两个选择ClearMemory、UninitializedMemory
*listData = new UnsafeList<T>(initialCapacity, allocator.Handle, options);//构造函数返回新的内存起始地址的指针
return listData;
}
public UnsafeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
{
Ptr = null;//数据所在
m_length = 0;//length和capacity分别类似于C# List的count和capacity
m_capacity = 0;
Allocator = allocator;
padding = 0;
SetCapacity(math.max(initialCapacity, 1));
if (options == NativeArrayOptions.ClearMemory && Ptr != null)
{
var sizeOf = sizeof(T);
UnsafeUtility.MemClear(Ptr, Capacity * sizeOf);//ClearMemory选项会清空已有的内存,本质是调用memset,将从ptr开始,长度为Capacity * sizeOf的内存的值设置为0
}
}
进一步看SetCapacity方法
public void SetCapacity(int capacity)
{
SetCapacity(ref Allocator, capacity);
}
void SetCapacity<U>(ref U allocator, int capacity) where U : unmanaged, AllocatorManager.IAllocator
{
CollectionHelper.CheckCapacityInRange(capacity, Length);//先检查设置的容量是否大于长度
var sizeOf = sizeof(T);//获取T类型占用的字节数
//CacheLineSize是当前平台的L1缓存行。L1缓存是离CPU最近的缓存层级,也是访问速度最快的缓存。它由数据缓存和指令缓存组成,分别用于存储数据和指令。缓存行是缓存的最小读写单位,一般是以字节为单位。
//L1缓存行的大小在不同的处理器架构上可能会有所不同,常见的大小是64字节。当CPU需要读取或写入数据时,它会首先检查L1缓存,如果所需数据在缓存行中,则可以直接访问,从而加快数据访问速度。如果数据不在缓存行中,则需要从更慢的内存层次(如L2缓存或主存)中获取。
//L1缓存行的设计目的是通过提前将数据和指令加载到高速缓存中,减少CPU等待数据的时间,从而提高计算机的性能。
var newCapacity = math.max(capacity, CollectionHelper.CacheLineSize / sizeO