Hello 小伙伴们大家好,今天和大家分享一下“字符串的优化”。
首先,通过跟踪 System.String 类,我们看到 string.Format 在最终的实现过程中是重新new了一个 StringBuilder。因此我们可以使用在对字符串进行 Format 操作的时候直接共用一个StringBuilder即可,具体实现如下:
private static StringBuilder stringBuilder = new StringBuilder();
public static string Format(string str, params object[] args)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.AppendFormat(str, args);
return stringBuilder.ToString();
}
StringBuilder.Format 的消耗其实也是挺大的,最终调用的是 AppendFormatHelper 这个函数,里面实现也是极其复杂,因此能尽量不用 Format 的时候尽量不用 Format。可以考虑使用 Concat 函数来代替。
string.Concat 每次使用都会重新生成一个 string,然后对其进行数据填充。当我们需要 Concat 的数据比较多的时候,里面每次都会判断填充的数据是否为null,循环体里都会有个objct转string的操作,其实大可不必如此。既然我们之前已经有一个通用的StringBuilder了,可以直接公用即可,每次使用之前清除掉旧数据,实现循环使用。如下图所示:
public static string Concat(string str1, string str2)
{
stringBuilder.Remove(0, stringBuilder.Length);
stringBuilder.Append(str1);
stringBuilder.Append(str2);
return stringBuilder.ToString();
}
如果有多个参数 str1、str2、str3等等的...就写多个 Concat 函数,如果在外部调用的时候遇到循环体可以在这边定义一个外部共享的 StringBuilder,具体如下:
private static StringBuilder shareStringBuilder = new StringBuilder();
public static StringBuilder GetShareStringBuilder()
{
shareStringBuilder.Remove(0, stringBuilder.Length);
return shareStringBuilder;
}
可能你看到这里,脑袋完全懵的,不知道我在做些什么。现在我直接上操作步骤:
首先新建两个脚本,分别为 GString、GStringSample,如下图所示:
GString 存放我们的字符串优化类,完整代码如下:
using System.Text;
namespace GameStartStudio
{
// 字符串优化类
public static class GString
{
private static readonly StringBuilder StrBuilder = new();
private static readonly StringBuilder ShareStrBuilder = new();
public static StringBuilder GetShareStringBuilder()
{
ShareStrBuilder.Remove(0, StrBuilder.Length);
return ShareStrBuilder;
}
public static string Format(string str, params object[] args)
{
StrBuilder.Remove(0, StrBuilder.Length);
StrBuilder.AppendFormat(str, args);
return StrBuilder.ToString();
}
public static string Concat(string str1, string str2)
{
StrBuilder.Remove(0, StrBuilder.Length);
StrBuilder.Append(str1);
StrBuilder.Append(str2);
return StrBuilder.ToString();
}
public static string Concat(string str1, string str2, string str3)
{
StrBuilder.Remove(0, StrBuilder.Length);
StrBuilder.Append(str1);
StrBuilder.Append(str2);
StrBuilder.Append(str3);
return StrBuilder.ToString();
}
}
}
GStringSample 存放测试代码,别忘了将代码挂载起来。
using UnityEngine;
using System.Text;
using UnityEngine.Profiling;
namespace GameStartStudio
{
public class GStringSample : MonoBehaviour
{
private void Update()
{
// 优化前
BeforeOptimize();
// 优化后
AfterOptimize();
}
string s1 = string.Empty;
private void BeforeOptimize()
{
Profiler.BeginSample("Before Optimize");
s1 = string.Empty;
for (int i = 0; i < 1000; i++)
{
s1 += i;
}
Profiler.EndSample();
}
private readonly StringBuilder _stringBuilder = GString.GetShareStringBuilder();
private void AfterOptimize()
{
Profiler.BeginSample("After Optimize");
_stringBuilder.Remove(0, _stringBuilder.Length);
for (int i = 0; i < 1000; i++)
{
_stringBuilder.Append(i);
}
Profiler.EndSample();
}
}
}
最后通过观察 Profiler Tab 来查看性能分析:
优化前GC是2.7MB
优化后GC只有27.1KB