有人可能会争辩说,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>();