日志
常规用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
string welcomeMessage = Utility.Text.Format("Hello Game Framework {0}.", Version.GameFrameworkVersion); // 打印调试级别日志,用于记录调试类日志信息 Log.Debug(welcomeMessage); // 打印信息级别日志,用于记录程序正常运行日志信息 Log.Info(welcomeMessage); // 打印警告级别日志,建议在发生局部功能逻辑错误,但尚不会导致游戏崩溃或异常时使用 Log.Warning(welcomeMessage); // 打印错误级别日志,建议在发生功能逻辑错误,但尚不会导致游戏崩溃或异常时使用 Log.Error(welcomeMessage); // 打印严重错误级别日志,建议在发生严重错误,可能导致游戏崩溃或异常时使用,此时应尝试重启进程或重建游戏框架 Log.Fatal(welcomeMessage); // 打印带有参数的日志 Log.Info("Hello Game Framework {0}.", Version.GameFrameworkVersion); |
参考手册
最佳实践
发布最终版本时选择打印合适级别的日志
日志的打印极其影响 CPU 时间且带来大量内存分配,故发布最终版本时,可以考虑仅打印错误以上级别的日志,甚至屏蔽所有日志。
为了完整地屏蔽掉日志开销,在书写日志时需注意格式,这里以开启仅打印错误及以上级别日志为例。
1 2 3 4 5 6 7 |
// 有内存开销,在开启仅打印错误及以上级别日志后,信息日志虽然不打印了,但 welcomeMessage 字符串临时变量的拼接会导致内存分配 string welcomeMessage = Utility.Text.Format("Hello Game Framework {0}.", Version.GameFrameworkVersion); Log.Info(welcomeMessage); // 在开启仅打印错误及以上级别日志后,以下两种写法均没有任何开销 Log.Info("Hello Game Framework {0}.", Version.GameFrameworkVersion); Log.Info(Utility.Text.Format("Hello Game Framework {0}.", Version.GameFrameworkVersion)); |
日志参数数量尽可能不要超过 3 个
日志参数超过 3 个(≥4)时,将会生成可变参数数组,导致额外的内存分配。
常见问题
打印日志后,控制台没有输出
为了兼顾发布后的性能,日志的开启是通过宏定义控制的,新工程中默认不包含任何日志宏定义,故没有日志输出。需通过以下菜单项,根据需要开启相应级别的日志。
引用池
为了降低因大量产生类对象而导致的堆内存碎片和分配时间开销,设计了引用池的概念,来将用完的对象清理并缓存起来,供后续使用。
常规用法
定义引用类
只有引用类才能使用引用池,若要定义引用类,定义为 IReference 接口,并实现 Clear 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class ReferenceExample : IReference { public ReferenceExample() { Sender = null; Args = new List<object>(); } public object Sender { get; private set; } public List<object> Args { get; private set; } public void Clear() { // IReference 必须实现 Clear 方法,注意实现完整,不要遗漏 Sender = null; Args.Clear(); } } |
从引用池获取引用
1 2 3 4 5 6 |
// 通过泛型方法获取引用 ReferenceExample instance1 = ReferencePool.Acquire<ReferenceExample>(); // 通过类型参数获取引用 IReference instance2 = ReferencePool.Acquire(typeof(ReferenceExample)); ReferenceExample instance3 = (ReferenceExample)instance2; |
将引用归还引用池
1 2 3 |
// 归还引用 ReferencePool.Release(instance1); ReferencePool.Release(instance2); |
向引用池中追加指定数量的引用
1 2 3 4 5 |
// 通过泛型方法向引用池中追加指定数量的引用 ReferencePool.Add<ReferenceExample>(10); // 通过类型参数向引用池中追加指定数量的引用 ReferencePool.Add(typeof(ReferenceExample), 10); |
从引用池中移除指定数量的引用
注:如果引用池中剩余数量少于指定数量,会清空引用池,并不引发错误。
1 2 3 4 5 |
// 通过泛型方法从引用池中移除指定数量的引用 ReferencePool.Remove<ReferenceExample>(10); // 通过类型参数从引用池中移除指定数量的引用 ReferencePool.Remove(typeof(ReferenceExample), 10); |
从引用池中移除所有的引用
1 2 3 4 5 |
// 通过泛型方法从引用池中移除所有的引用 ReferencePool.RemoveAll<ReferenceExample>(); // 通过类型参数从引用池中移除所有的引用 ReferencePool.RemoveAll(typeof(ReferenceExample)); |
清除所有引用池
1 |
ReferencePool.ClearAll(); |
获取引用池的数量
1 |
int count = ReferencePool.Count; |
参考手册
最佳实践
对频繁生成的对象,使用引用池
对频繁生成的对象,使用引用池能够显著减少新对象的内存分配。
在引用类中定义静态 Create 方法
建议在引用类中定义静态的 Create 方法,将 Acquire 操作封装起来。如在 ReferenceExample 中定义以下方法,方便外部使用。
1 2 3 4 5 6 7 8 9 |
public static ReferenceExample Create(object sender, object arg0, object arg1) { // 建议实现一个静态的 Create 方法,将 Acquire 操作封装起来 ReferenceExample referenceExample = ReferencePool.Acquire<ReferenceExample>(); referenceExample.Sender = sender; referenceExample.Args.Add(arg0); referenceExample.Args.Add(arg1); return referenceExample; } |
1 2 |
// 方便了外部使用 ReferenceExample referenceExample = ReferenceExample.Create(this, null, null); |
常见问题
获取对象且使用完成后,忘记归还对象或者归还时机不正确
注意 Acquire 一定要在合适的时机 Release。
泛型类的 Clear 方法实现不完整
使用引用池技术的对象是会被复用的。当回收某个对象时,不完整的 Clear 实现无法清理干净对象中的数据,进而将脏数据带到下一次复用对象的逻辑中去,这可能导致难以追踪的 BUG。