有人可能会争辩说,Unity中的资源消耗比谦卑的字符串还多,但这是早期更容易解决的方面之一。
大多数字符串操作会生成少量垃圾,但是如果在单个更新过程中多次调用了这些操作,则会堆积它们。随着时间的流逝,它将触发自动垃圾收集,这可能会导致可见的CPU峰值。
考虑以下示例。
string[] StringKeys = new string[] { "Key0", "Key1", "Key2" }; void Update() { for (var i = 0; i < 3; i++) { // 缓存,不产生垃圾 Debug.Log(StringKeys[i]); } for (var i = 0; i < 3; i++) { // 不缓存,每个周期都垃圾 Debug.Log("Key" + i); } // 最节省内存的方法是根本不创建缓存,而使用文字或常量。 // 但是,它不一定是最易读或美观的方法。 Debug.Log("Key0"); Debug.Log("Key1"); Debug.Log("Key2"); }
它可能看起来很愚蠢且多余,但是如果您使用的是Shaders,则可能会遇到诸如此类的情况。缓存密钥将有所作为。
请注意,字符串文字和常量不会产生任何垃圾,因为它们是静态注入程序堆栈空间的。如果您在运行时生成字符串,并且可以确保每次生成相同的字符串,如上面的示例,那么缓存肯定会有所帮助。
对于其他情况,每次生成的字符串都不相同,除了生成这些字符串,没有其他选择。这样,每次手动生成字符串的内存峰值通常可以忽略不计,除非一次生成数万个字符串。
对调试消息进行字符串操作,即 Debug.Log("对象名称: " + obj.name)很好,在开发过程中无法避免。但是,重要的是要确保无关的调试消息不会最终发布在产品中。
一种方法是在调试调用中使用Conditional属性。这不仅删除了方法调用,还删除了其中所有的字符串操作。
using UnityEngine; using System.Collections; public class ConditionalDebugExample: MonoBehaviour { IEnumerator Start() { while(true) { // 该消息将在“编辑器”中弹出,但不会在内部版本中弹出 Log("Elapsed: " + Time.timeSinceLevelLoad); yield return new WaitForSeconds(1f); } } [System.Diagnostics.Conditional("UNITY_EDITOR")] void Log(string Message) { Debug.Log(Message); } }
这是一个简化的示例。您可能需要花费一些时间来设计更完善的日志记录例程。
这是次要的优化,但值得一提。比较字符串比人们想象的要复杂得多。系统默认会尝试考虑文化差异。您可以选择使用简单的二进制比较,它执行起来更快。
// 更快的字符串比较 if (strA.Equals(strB, System.StringComparison.Ordinal)) {...} // 相比 if (strA == strB) {...} // 减少开销 if (!string.IsNullOrEmpty(strA)) {...} // 相比 if (strA == "") {...} // 更快的查询 Dictionary<string, int> myDic = new Dictionary<string, int>(System.StringComparer.Ordinal); // 相比 Dictionary<string, int> myDictionary = new Dictionary<string, int>();