U3d内存优化(一)之UILabel使用String的问题

来源:互联网 发布:淘宝几个好评一颗心 编辑:程序博客网 时间:2024/06/04 18:12

问题发现:当在Upade中使用倒计时的时候,会出现大量的内存分配,这个内存分配主要是tostring()或string.format引起的,这就导致了频繁的GC。
1. 先看4种状态下的获取string的内存比较(int.tostring(),SpeedString,char.tostring,StringBuilder.Tostring())
这里写图片描述
代码如下:

        public void Update()        {            IntTostring();            StringBuilderTostring();            CharsTostring();            SpeedStringToString();        }         System.Text.StringBuilder builder = new System.Text.StringBuilder(50);        private void IntTostring(int time = 10000)        {            string timeStr = time.ToString();        }        private void StringBuilderTostring(int time =1)        {            builder.Length = 0;            builder.Append("1000");            string timeStr = builder.ToString();        }        private void CharsTostring(int time = 1)        {            chars[0] = (char)(time + '0');            chars[1] = (char)(time + '0');            chars[2] = (char)(time + '0');            string timeStr = chars.ToString();        }        int i =10000;        private void SpeedStringToString()        {            speedStr.Clear();            speedStr.Append(Random.Range(0,100000));            string str = speedStr.string_base;        }        SpeedString speedStr = new SpeedString(10);        char[] chars = new char[4];

SpeedString的实现(参考http://forum.unity3d.com/threads/stringbuilder-problem.122262/)

  public class SpeedString    {        public string string_base;        public System.Text.StringBuilder string_builder;        private char[] int_parser = new char[11];        public SpeedString(int capacity)        {            string_builder = new System.Text.StringBuilder(capacity, capacity);            string_base = (string)string_builder.GetType().GetField(                "_str",                System.Reflection.BindingFlags.NonPublic |                System.Reflection.BindingFlags.Instance).GetValue(string_builder);        }        private int i;        public void Clear()        {            string_builder.Length = 0;            for (i = 0; i < string_builder.Capacity; i++)            {                string_builder.Append(' ');            }            string_builder.Length = 0;        }        //public void Draw(ref string text){        //    text.text = "";        //    text.text = string_base;        //    text.cachedTextGenerator.Invalidate();        //}        public void Append(string value)        {            string_builder.Append(value);        }        int count;        public void Append(int value)        {            if (value >= 0)            {                count = ToCharArray((uint)value, int_parser, 0);            }            else            {                int_parser[0] = '-';                count = ToCharArray((uint)-value, int_parser, 1) + 1;            }            for (i = 0; i < count; i++)            {                string_builder.Append(int_parser[i]);            }        }        private static int ToCharArray(uint value, char[] buffer, int bufferIndex)        {            if (value == 0)            {                buffer[bufferIndex] = '0';                return 1;            }            int len = 1;            for (uint rem = value / 10; rem > 0; rem /= 10)            {                len++;            }            for (int i = len - 1; i >= 0; i--)            {                buffer[bufferIndex + i] = (char)('0' + (value % 10));                value /= 10;            }            return len;        }    }
  1. 选择使用SpeedString来在Update()中进行赋值(或战斗中血条的显示及技能CD)
    使用NGUI遇到的问题:
    (1). UILabel.text = str不会刷新界面。
    原因:NGUI中text赋值的实现如 代码2:(由于使用SpeedString后,给NGUI反复赋值都是使用的同一个string内存空间(SpeedString.string_base),因此mText == value始终为true,为了刷新界面,提出更改的部分,实现如 代码3)

代码2

public string text    {        get        {            return mText;        }        set        {            if (mText == value) return;            if (string.IsNullOrEmpty(value))            {                if (!string.IsNullOrEmpty(mText))                {                    mText = "";                    MarkAsChanged();                    ProcessAndRequest();                }            }            else if (mText != value)            {                mText = value;                MarkAsChanged();                ProcessAndRequest();            }            if (autoResizeBoxCollider) ResizeCollider();        }    }

代码3

 public void SetText(string _text, bool bForce = false)    {//3.7.7        if (bForce)        {            if(mText != _text)                mText = _text;            MarkAsChanged();            ProcessAndRequestForce();            if (autoResizeBoxCollider) ResizeCollider();        }        else            this.text = _text;    }

对NGUI 3.9.7版本的,使用如下代码:

 public void SetText(string _text, bool bForce = false)    {        if (bForce)        {            if (mText != _text || mProcessedText!=_text)            {                mText = _text;                mProcessedText = _text;            }            MarkAsChanged();            ProcessAndRequestForce();            if (autoResizeBoxCollider) ResizeCollider();        }        else            this.text = _text;    }

ProcessAndRequestForce是对NGUI中的又已修改,因为使用的原ProcessAndRequest过程发现,UILabel中的void ProcessText (bool legacyMode, bool full)会导致内存分配,为了避免,修改了这个函数

void ProcessText (bool legacyMode, bool full,bool bFroce = false)    {        //........省略.....................        // Wrap the text        bool fits = true;       if ( !bFroce || (mText != mProcessedText && bFroce))           fits = NGUIText.WrapText(mText, out mProcessedText, true);       //........省略.....................    }     void ProcessAndRequestForce()    {#if UNITY_EDITOR        if (!Application.isPlaying && !NGUITools.GetActive(this)) return;        if (!mAllowProcessing) return;#endif        if (ambigiousFont != null) ProcessText(false, true,true);    }

最后就将给UILabel赋值的内存分配降低到了0。

///////
关于 读者提出的修改SetText的方法的问题:

public void SetText(string _text,bool bForce = false)    {        if(bForce)        {            if (mText != _text)                mText = _text;            MarkAsChanged();            ProcessAndRequestForce();            if (autoResizeBoxCollider) ResizeCollider();        }        {            text = _text;        }    }    public void SetTextNew(string _text, bool bForce = false)    {        if (bForce)        {            if (mText != _text)            {                 mText = _text;                ProcessAndRequestForce();            }            MarkAsChanged();            if (autoResizeBoxCollider) ResizeCollider();        }        {            text = _text;        }    }

原代码的测试结果
这里写图片描述
读者的测试结果
这里写图片描述
内存中多了UIPanelLateUpdate中的内存消耗。
谢谢 hyf2713 提出不同的方法,有问题希望大家提出,或者有更好的修改方法

0 0