string读取字符串字符时出错_Unity字符串String优化

9e19719ce17d09609f65c75dfb428ea0.png

《游戏AI程序设计实战》作者

Github:onelei - Overview

CSDN:https://blog.csdn.net/onelei1994

QQ群:754814245

========================

Unity字符串String优化

System.String

a07323724a88723a323f598596cb6ae3.png

通过跟踪Unity的string,我们看到了Unity给我们提供的各种string接口。发现该代码来自mscorlib.dll,通过路径直接找到该dll

EditorDataMonoBleedingEdgelibmonounitymscorlib.dll

我们通过dnSpy这个工具来对mscorlib.dll进行反编译。打开dnSpy软件通过“文件/打开”找到该dll,将其加载进来。

e5a880b14b3e1fdbad6435d98a61b2ad.png

根据string的命名空间直接找到“System”的“String”类

4277497d478123ad49a94587a746ab23.png

string.Format

19faa1abc6f5ce2a422f518dbafe454c.png

通过反编译,我们看到string.Format在最终的实现过程中是重新new了一个StringBuilder。因此我们可以使用在对字符创进行Format操作的时候直接共用一个StringBuilder即可,具体实现如下

private static StringBuilder stringBuilder = new StringBuilder();

    public static string Format(string src, params object[] args)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.AppendFormat(src, args);
        return stringBuilder.ToString();
    }

StringBuilder.Format的消耗也是挺大的,我们看看内部实现

35753a58a65dad50f4801f11e0091f01.png

8e983080627476490444337ddcc44c8a.png

最终调用的是AppendFormatHelper这个函数,里面实现也是极其复杂,这里也只是截取了一小部分,因此能尽量不用Format的时候尽量不用Format。可以考虑使用Concat函数来代替。

string.Concat

d54454d503b4f3c9a2ba3883c339c58a.png

string.Concat每次使用都会重新生成一个string,然后对其进行数据填充。当我们需要Cocat的数据比较多的时候实际上是调用下面这个方法

92e3f7f5a94fa208dbaf214dd37e7d5c.png

1e3313f8734b091874adef1850e4382d.png

里面每次都会判断填充的数据是否为null,循环体里都会有个objct转string的操作,其实大可不必如此。既然我们之前已经有一个通用的StringBuilder了,可以直接公用即可,每次使用之前清除掉旧数据,实现循环使用。

public static string Concat(string s1,string s2)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.Append(s1);
        stringBuilder.Append(s2);
        return stringBuilder.ToString();
    }

如果有多个参数s1,s2...就写多个Concat函数,如果在外部调用的时候遇到循环体可以在这边定义一个外部共享的StringBuilder,具体如下

private static StringBuilder shareStringBuilder = new StringBuilder();

    public static StringBuilder GetShareStringBuilder()
    {
        shareStringBuilder.Remove(0, stringBuilder.Length);
        return shareStringBuilder;
    }

外部调用如下

public void Test()
    {
        StringBuilder stringBuilder = QString.GetShareStringBuilder();
        for (int i = 0; i < 100; i++)
        {
            stringBuilder.Append(i);
        }
        Debug.Log(stringBuilder.ToString());
    }

完整代码如下

using System.Text;
using UnityEngine;

/// <summary>
/// 字符串优化类
/// </summary>
public class QString
{
    private static StringBuilder stringBuilder = new StringBuilder();
    private static StringBuilder shareStringBuilder = new StringBuilder();

    public static StringBuilder GetShareStringBuilder()
    {
        shareStringBuilder.Remove(0, stringBuilder.Length);
        return shareStringBuilder;
    }

    public static string Format(string src, params object[] args)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.AppendFormat(src, args);
        return stringBuilder.ToString();
    }

    public static string Concat(string s1, string s2)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.Append(s1);
        stringBuilder.Append(s2);
        return stringBuilder.ToString();
    }

    public static string Concat(string s1, string s2, string s3)
    {
        stringBuilder.Remove(0, stringBuilder.Length);
        stringBuilder.Append(s1);
        stringBuilder.Append(s2);
        stringBuilder.Append(s3);
        return stringBuilder.ToString();
    }
}

public class QStringSample
{
    public void Test()
    {
        StringBuilder stringBuilder = QString.GetShareStringBuilder();
        for (int i = 0; i < 100; i++)
        {
            stringBuilder.Append(i);
        }
        Debug.Log(stringBuilder.ToString());
    }
}

通过实验进行数据对比

using UnityEngine;
using System.Text;
using UnityEngine.Profiling;

public class QStringTest : MonoBehaviour
{
    private void Update()
    {
        Before();

        After();
    }

    string s1 = string.Empty;
    private void Before()
    {
        Profiler.BeginSample("Before");
        s1 = string.Empty;
        for (int i = 0; i < 100; i++)
        {
            s1 += i;
        }
        Profiler.EndSample();
    }

    StringBuilder stringBuilder = QString.GetShareStringBuilder();
    private void After()
    {
        Profiler.BeginSample("After");
        stringBuilder.Remove(0, stringBuilder.Length);
        for (int i = 0; i < 100; i++)
        {
            stringBuilder.Append(i);
        }
        Profiler.EndSample();
    }
}

d7d4ddb3f5c32b23b8a48b730a48ee09.png

优化前的GC是25.2KB 优化后的GC只有2.9k

==================

欢迎支持我的新书《游戏AI程序设计实战》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值