ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

07 引用池

2021-11-03 22:35:40  阅读:163  来源: 互联网

标签:07 void collection class 引用 new public


引用池,和对象池的概念非常像,但是对象池服务于具体的GameObject对象,而引用池服务于普通的C#类。举个例子,如果打开UI的时候你想传递一些数据,最简单的方法就是

class UIData
{
    //...
}
        
main()
{
	UIData data = new UIData();
	OpenUI(data);
}

这个方法是我在上一节末尾提到的,即定义一个数据类型,然后打开UI的时候直接传进函数里,说实话我感觉这样做...其实也挺好的...很简单直观,感觉也没啥问题...

咳咳,但是!但是你想挑毛病还是有的...比如这个data,每次我们打开一个UI,都需要new一个,然后传参数进去。众所周知,频繁的new肯定是不得劲的!所以引用池避免的就是这个问题...

是不是很对象池很像?对象池避免的是频繁生成游戏对象,而引用池避免的是频繁生成普通对象

引用对象接口

/// 引用接口,实现这个接口的类会被引用池管理
public interface IReference
{
    /// 归还引用时的清理方法
    void Clear();
}

想要某些对象能够被统一的管理,那必然需要一个统一的接口,关于其中的Clear方法,则是当我们归还这个对象时调用的,类似于清空数据,恢复默认状态

引用集合

用于收集同一类的所有对象

/// 引用集合,收集同一类型的所有引用,会被引用池直接管理
public class ReferenceCollection   
{       
    #region Private      
    /// 这个队里里储存里我们目前所有可以使用的对象
    private Queue<IReference> m_References;       
    #endregion
       
    #region 构造方法        
    public ReferenceCollection()        
    {            
        m_References = new Queue<IReference>();      
    }        
    #endregion    
}

主要只有两个方法,获取引用和归还引用

public class ReferenceCollection   
{       
#region Public 接口方法
        /// 获取指定类型的引用,核心就是,如果引用队列内有空引用,那么直接获取,如果没有,那么new一个
        public T Acquire<T>() where T : class, IReference, new()
        {
            // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
			#if UNITY_EDITOR
            var trans = SGFEntry.Instance.transform.Find("ReferenceManager");
            var collection = trans.Find(typeof(T).FullName);
			#endif
            // =========================================================================
            
            // 这部分是获取引用的算法
            lock (m_References)
            {
                if (m_References.Count > 0)
                {
                    // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
					#if UNITY_EDITOR
                    if (collection != null)
                    {
                        // 找到一个active的孩子,设置为false
                        for (int i = 0; i < collection.childCount; i++)
                        {
                            if (collection.GetChild(i).gameObject.activeSelf)
                            {
                                collection.GetChild(i).gameObject.SetActive(false);
                                break;
                            }
                        }
                    }
					#endif
                    // =========================================================================
                    
                    return m_References.Dequeue() as T;
                }
            }
            
            // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
			#if UNITY_EDITOR
            if (collection != null)
            {
                GameObject go = new GameObject();
                go.name = "reference";
                go.transform.SetParent(collection);
                go.gameObject.SetActive(false);
            }
			#endif
            // =========================================================================
            
            return new T();
        }
        
        /// 释放引用
        public void Release<T>(T reference) where T : class, IReference
        {
            // 会在这里调用引用对象的Clear方法,清空数据
            reference.Clear();
            lock (m_References)
            {
                // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
				#if UNITY_EDITOR
                var trans = SGFEntry.Instance.transform.Find("ReferenceManager");
                var collection = trans.Find(typeof(T).FullName);
                if (collection != null)
                {
                    for (int i = 0; i < collection.childCount; i++)
                    {
                        if (!collection.GetChild(i).gameObject.activeSelf)
                        {
                            collection.GetChild(i).gameObject.SetActive(true);
                            break;
                        }
                    }
                }
				#endif
                // =========================================================================
                
                m_References.Enqueue(reference);
            }
        }

        /// 删除所有引用
        public void RemoveAll()
        {
            lock (m_References)
            {
               m_References.Clear();
            }
        }

#endregion
}

引用池管理器

注意,上面写的引用收集,只是收集了同一类的对象,或者说,是某一种引用。而我们的游戏里当然不可能只有这么一种对象,因此,我们还是需要用一个管理器来管理所有的引用收集器的。

public class ReferenceManager : ManagerBase
{
#region Private
    
        /// 维护所有的引用集合,每一个集合内有多个同类型引用
        private Dictionary<string, ReferenceCollection> s_ReferenceCollections;

#endregion

#region 构造函数

        public ReferenceManager()
        {
            s_ReferenceCollections = new Dictionary<string, ReferenceCollection>();
        }

#endregion
}    

基本的获取与归还

public class ReferenceManager : ManagerBase
{
#region Public 接口方法

        /// 从引用集合获取引用
        public  T Acquire<T>() where T : class, IReference, new()
        {
            // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
			#if UNITY_EDITOR
            var trans = SGFEntry.Instance.transform.Find("ReferenceManager");
            var collection = trans.Find(typeof(T).FullName);
            if (collection == null)
            {
                GameObject go = new GameObject();
                go.name = typeof(T).FullName;
                go.transform.SetParent(trans);
            }
			#endif
            // =========================================================================
            
            return GetReferenceCollection(typeof(T).FullName).Acquire<T>();
        }
        
        /// 将引用归还引用集合
        public  void Release<T>(T reference) where T : class, IReference
        {
            if (reference == null)
            {
                throw new Exception("要归还的引用为空...");
            }

            GetReferenceCollection(typeof(T).FullName).Release(reference);
        }

        /// 清除所有引用集合
        public  void ClearAll()
        {
            lock (s_ReferenceCollections)
            {
                foreach (KeyValuePair<string, ReferenceCollection> referenceCollection in s_ReferenceCollections)
                {
                    referenceCollection.Value.RemoveAll();
                }
 
                s_ReferenceCollections.Clear();
            }
        }
        
        /// 从引用集合中移除所有的引用
        public  void RemoveAll<T>() where T : class, IReference
        {
            GetReferenceCollection(typeof(T).FullName).RemoveAll();
        }

#endregion

#region Private 工具方法

        /// 获取引用集合,实际上获取引用的方法
        private  ReferenceCollection GetReferenceCollection(string fullName)
        {
            ReferenceCollection referenceCollection = null;
            lock (s_ReferenceCollections)
            {
                if (!s_ReferenceCollections.TryGetValue(fullName, out referenceCollection))
                {
                    referenceCollection = new ReferenceCollection();
                    s_ReferenceCollections.Add(fullName, referenceCollection);
                }
            }
 
            return referenceCollection;
        }
        
#endregion

} 

重写一些接口方法,还有Update方法

public class ReferenceManager : ManagerBase
{
#region Private

        /// 清理间隔,每过一段时间,就会清空队列里的引用(还在队列里,说明是空引用),m_temp用于实际计算,想要修改清理间隔可以修改clearInterval
        private float m_ClearInterval = ManagerConfig.ClearInterval;
        private float m_Temp;
        
#endregion
    
#region Override

        public override int Priority
        {
            get
            {
                return ManagerPriority.ReferenceManager.GetHashCode();
            }
        }

        public override void Init()
        {
            m_Temp = m_ClearInterval;
        }

    	// 每过一定时间,就会把引用池清空
        public override void Update(float time)
        {
            m_Temp -= time;
            if (m_Temp < 0f) 
            {
                // =========== 这一部分是用于DEBUG的,会在Hierachy中显示出当前的引用状态 ==========
				#if UNITY_EDITOR
                var trans = SGFEntry.Instance.transform.Find("ReferenceManager");
                for (int i = 0; i < trans.childCount; i++)
                {
                    GameObject.DestroyImmediate(trans.GetChild(i).gameObject);
                }
				#endif
                // =========================================================================
                
                foreach (var e in s_ReferenceCollections)
                {
                    e.Value.RemoveAll();
                }

                m_Temp = m_ClearInterval;
            }
        }
        
#endregion
}  

测试一下~

public class TestRef : IReference
{
    public string testName="引用池测试";
    public void Clear()
    {
        Debug.Log("TestRef被清空了");
    }
}

public class test : MonoBehaviour
{
    private ReferenceManager referenceManager;
    void Start()
    {
        referenceManager = SGFEntry.Instance.GetManager<ReferenceManager>();
        var tempRef = referenceManager.Acquire<TestRef>();
        Debug.Log(tempRef.testName);
        // 一定要在合适的事件归还引用,如果只获取,不归还,那和无限new没有任何区别
        referenceManager.Release(tempRef);
    }
}

11

标签:07,void,collection,class,引用,new,public
来源: https://www.cnblogs.com/Logarius/p/15506006.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有