Unity、C#、Lua面试题整理二、(持续更新)

二十四:C#,GC的原理

答:     1.被分配内存空间的对象最有可能被释放。在方法执行时,就需要为该方法的对象分配内存空间,搜索最近分配的对象集合有助于花费最少的代价来尽可能多地释放内存空间。
2.生命期最长的对象释放的可能性最小,经过几轮垃圾回收后,对象仍然存在,搜索它时就需要进行大量的工作,却只能释放很小的一部分空间。
3.同时被分配内存的对象通常是同时使用,将它们彼此相连有助于提高缓存性能和回收效率。
C#中的回收器是分代的垃圾回收器(Gererational GarbageCollector) 它将分配的对象分为3个类别或代。(可用GC.GetGeneration方法返回任意作为参数的对象当前所处的代)最近被分配内存的对象被放置于第0代,因为第0代很小,小到足以放进处理器的二级(L2)缓存,所以它能够提供对对象的快速存取。经过一轮垃圾回收后,仍然保留在第0代中的对象被移进第1代中,再经过一轮垃圾内存回收后,仍然保留在第1代中的对象则被移进第2代中,第2代中包含了生存期较长的对象。
      在C#中值类型是在堆栈中分配内存,它们有自身的生命周期,所以不用对它们进行管理,会自动分配和释放。而引用类型是在堆中分配内存的。所以它的分配和释放就需要像回收机制来管理。C#为一个对象分配内存时,托管堆可以立即返回新对象所需的内存,因为托管堆类似于简单的字节数组,有一个指向第一个可用内存空间的指针,指针像游标一样向后移动,一段段内存就分配给了正在运行的程序的对象。在不需要太多垃圾回收的程序小,托管堆性能优于传统的堆。
      当第0代中没有可以分配的有效内存时,就触发了第0代中的一轮垃圾回收,它将删除那些不再被引用的对象,并将当前正在使用的对象移至第1代。而当第0代垃圾回收后依然不能请求到充足的内存时,就启动第1代垃圾回收。如果对各代都进行了垃圾回收后仍没有可用的内存就会引发一个OutOfMemoryException异常。

      在垃圾回收时尽量避免使用finallize来回收资源,这样会造成两车垃圾回收,影响效率

      垃圾回收器使用名为“终止队列”的内部结构跟踪具有 Finalize 方法的对象。每次您的应用程序创建具有 Finalize 方法的对象时,垃圾回收器都在终止队列中放置一个指向该对象的项。托管堆中所有需要在垃圾回收器回收其内存之前调用它们的终止代码的对象都在终止队列中含有项。(实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用 Finalize 方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。)

二十五:FSM(状态机具体怎么实现):

答: 初始化各状态机的名称或ID(一般使用枚举),设定每种状态人物对应的行为(编写相应的脚本),状态切换(切换条件,切换回调)

二十六:Lua如何与C#进行交互的

答: Lua调用C#:

  1.打标签[LuaCallC#]:首先生成C#源文件所对应的Wrap文件,由Lua文件调用Wrap文件,再由Wrap文件调用C#文件;
   
  2.不打标签:通过反射方式当索引系统API、dll库或者第三方库时,如果无法将代码的具体实现进行代码生成,可采用此方式实现交互。缺点:执行效率低。

C#调用Lua:打标签[C#CallLua]:由C#先将数据放入栈中,由lua去栈中获取数据,然后返回数据对应的值到栈顶,再由栈顶返回至C#

C#生成Bridge文件,Bridge调dll文件(dll是用C写的库),先调用lua中dll文件,由dll文件执行lua代码
C#->Bridge->dll->Lua  OR   C#->dll->Lua

二十七:Lua如何在项目中使用:

答:通过一个C#脚本调用 Lua入口文件(Main.lua),加载全局模块,比如UI模块,事件模块,table工具类模块,class类模块,单例管理类模块等等,部分模块用C#实现,Lua调用C#

二十八:Lua实现单例模式:

Singleton={}
function Singleton:new(o)
    o=o or {}
    setmetatable(o,self)
    self.__index=self
    return o
end

function Singleton:Instance()
    if self.instance == nil then
    self.instance = self:new()
    end
    return self.instance
end

--[[
    优点:确保所有对象都访问唯一实例
    缺点:每次对象请求引用时都要检查是否存在类的实例。  必须记住自己不能使用new关键字实例化对象。
--]]
	

二十九:内存是如何优化的:

答:1.压缩自带类库;

2.将暂时不用的以后还需要使用的物体影藏起来,不要直接Destroy掉,频繁调用销毁的物体使用对象池

3.释放AssetBundle占用的资源

4.降低模型的片面数,降低模型的骨骼数量,降低贴图大小,尽量使用图集

5.使用光照贴图,使用多层次细节(LOD),使用着色器(Shader),使用预设体(Prefabs)

6.尽量减少unity的回调函数,即使空着的Update,Fixupdate函数也不要留着

三十:SDK接入怎么接入的?Plugins目录的作用:

答: 新建一个Android library工程,按照平台给的SDK文档配置AndroidManifest.xml,然后添加SDK的aar或者jar库到libs目录下,不要忘了去Unity安装目录找到classes.jar,放入libs目录下,然后配置gradle。

 接着就开始写SDK代码。写完之后直接生成aar库,直接导入到Unity就可以用了。

Plugins目录:Plugins里存放的SDK,库文件能够优先被编译

Windows     .dll 动态链接库

Linux Mac Android IOS   .so 共享函数库

三十一:地形优化插件

答:T4M插件

三十二:对象池的好处是啥?真的会减少内存占用嘛

答:优缺点:空间换时间,用额外的内存消耗减少创建物体的时间,保证运行的流畅度

三十三:C#索引器的实现

答:索引器允许类和结构的实例就像数组一样,直观的引用类中的成员。一索引器可以重载,一个类可以有多个索引器,索引的参数可以是任何类型,而不像整数参数类型只能是整数

 

private string[] name = new string[10];

//定义索引器,必须以this关键字定义,设置name字段的索引值为0,address字段的索引值为

public string this[int index]{

get{return name[index];}

set{this.name[index] = value;}

}

三十四:相机移动应该放在哪个函数里:

答:LateUpdate,是在所有的Update结束后才调用,比较适合用于命令脚本的执行。官网上例子是摄像机的跟随,都是所有的Update操作完才进行摄像机的 跟进,不然就有可能出现摄像机已经推进了,但是视角里还未有角色的空帧出现。   

三十五:矩阵相乘的意义及注意点

答:用于表示线性的变换:旋转,缩放,投影,平移,仿射

注意矩阵的蠕变:误差的积累

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值