引用计数是一个很好用的技术概念,不要被这个名字吓到了。首先来讲讲引用计数是干嘛的。

引用计数使用场景

有一间黑色的屋子,里边有一盏灯。当第一个人进屋的时候灯会打开,之后的人进来则不用再次打开了,因为已经开过了。当屋子里的所有人离开的时候,灯则会关闭。

我们先定义灯的对象模型:

	class Light
{
public void Open()
{
Log.I("灯打开了");
} public void Close()
{
Log.I("灯关闭了");
}
}

很简单就是两个方法而已。

再定义屋子的类,屋子应该持有一个Light的对象,并且要记录人们的进出。当有人进入,进入后当前房间只有一个人的时候,要把灯打开。当最后一个人离开的时候灯要关闭。

代码如下:

	class Room
{
private Light mLight = new Light(); private int mPeopleCount = 0; public void EnterPeople()
{
if (mPeopleCount == 0)
{
mLight.Open();
} ++mPeopleCount; Log.I("一个人走进房间,房间里当前有{0}个人",mPeopleCount);
} public void LeavePeople()
{
--mPeopleCount; if (mPeopleCount == 0)
{
mLight.Close();
} Log.I("一个人走出房间,房间里当前有{0}个人", mPeopleCount);
}
}

很简单,我们来看下测试代码。

var room = new Room();
room.EnterPeople();
room.EnterPeople();
room.EnterPeople(); room.LeavePeople();
room.LeavePeople();
room.LeavePeople(); room.EnterPeople();

看下输出的结果:

灯打开了
一个人走进房间,房间里当前有1个人
一个人走进房间,房间里当前有2个人
一个人走进房间,房间里当前有3个人
一个人走出房间,房间里当前有2个人
一个人走出房间,房间里当前有1个人
一个人走出房间,房间里当前有0个人
灯关闭了
灯打开了
一个人走进房间,房间里当前有1个人

OK.以上就是引用计数这项计数的使用场景的所有代码。

测试的代码比较整齐,很容易算出来当前有多少个人在屋子里,所以看不出来引用计数的作用。但是在日常开发中,我们可能会把EnterPeople和LeavePeople散落在工程的各个位置。这样就很难统计,这时候引用计数的作用就很明显了,它可以自动帮助你判断什么时候进行开灯关灯操作,而你不用写开关灯的一行代码。

这个例子比较接近生活,假如笔者再换个例子,我们把Light对象换成资源对象,其开灯对应加载资源,关灯对应卸载资源。而屋子则是对应资源管理器,EnterPeople对应申请资源对象,LeavePeople对应归还资源对象。这样你只管在各个界面中申请各个资源,只要在界面关闭的时候归还各个资源对象就可以不用关心资源的加载和卸载了,可以减轻大脑的负荷。

简易计数器实现:

计数器的接口很简单,代码如下:

	public interface IRefCounter
{
int RefCount { get; } void Retain(object refOwner = null);
void Release(object refOwner = null);
}

Retain是增加引用计数(RefCount+1),Release减去一个引用计数(RefCount—)。

在接下来的具体实现中,当RefCount降为0时我们需要捕获一个事件,这个事件叫OnZeroRef。

代码如下:

	public class SimpleRC : IRefCounter
{
public SimpleRC()
{
RefCount = 0;
} public int RefCount { get; private set; } public void Retain(object refOwner = null)
{
++RefCount;
} public void Release(object refOwner = null)
{
--RefCount;
if (RefCount == 0)
{
OnZeroRef();
}
} protected virtual void OnZeroRef()
{
}
}

以上就是简易引用计数器的所有实现了。

接下来我们用这个引用计数器,重构灯的使用场景的代码。

	class Light
{
public void Open()
{
Log.I("灯打开了");
} public void Close()
{
Log.I("灯关闭了");
}
} class Room : SimpleRC
{
private Light mLight = new Light(); public void EnterPeople()
{
if (RefCount == 0)
{
mLight.Open();
} Retain(); Log.I("一个人走进房间,房间里当前有{0}个人",RefCount);
} public void LeavePeople()
{
// 当前还没走出,所以输出的时候先减1
Log.I("一个人走出房间,房间里当前有{0}个人", RefCount - 1); // 这里才真正的走出了
Release();
} protected override void OnZeroRef()
{
mLight.Close();
}
}

测试代码和之前的一样,我们看下测试结果:

灯打开了
一个人走进房间,房间里当前有1个人
一个人走进房间,房间里当前有2个人
一个人走进房间,房间里当前有3个人
一个人走出房间,房间里当前有2个人
一个人走出房间,房间里当前有1个人
一个人走出房间,房间里当前有0个人
灯关闭了
灯打开了
一个人走进房间,房间里当前有1个人

好了,今天就到这里

转载请注明地址:凉鞋的笔记:liangxiegame.com

更多内容

Unity 游戏框架搭建 (二十二) 简易引用计数器的更多相关文章

  1. Unity 游戏框架搭建 2019 (十八~二十) 概率函数 & GameObject 显示、隐藏简化 & 第二章 小结与快速复习

    在笔者刚做项目的时候,遇到了一个需求.第一个项目是一个跑酷游戏,而跑酷游戏是需要一条一条跑道拼接成的.每个跑道的长度是固定的,而怪物的出现位置也是在跑道上固定好的.那么怪物出现的概率决定一部分关卡的难 ...

  2. # Unity 游戏框架搭建 2019 (十六、十七) localPosition 简化与Transform 重置

    在上一篇我们收集了一个 屏幕分辨率检测的一个小工具.今天呢再往下接着探索. 问题 我们今天在接着探索.不管是写 UI 还是写 GamePlay,多多少少都需要操作 Transform. 而在笔者刚接触 ...

  3. Unity 游戏框架搭建 (十) QFramework v0.0.2小结

    从框架搭建系列的第一篇文章开始到现在有四个多月时间了,这段时间对自己来说有很多的收获,好多小伙伴和前辈不管是在评论区还是私下里给出的建议非常有参考性,在此先谢过各位. 说到是一篇小节,先列出框架的概要 ...

  4. Unity 游戏框架搭建 (十六) v0.0.1 架构调整

    背景: 前段时间用Xamarin.OSX开发一些工具,遇到了两个问题. QFramework的大部分的类耦合了Unity的API,这样导致不能在其他CLR平台使用QFramework. QFramew ...

  5. Unity 游戏框架搭建 (十三) 无需继承的单例的模板

    之前的文章中介绍的Unity 游戏框架搭建 (二) 单例的模板和Unity 游戏框架搭建 (三) MonoBehaviour单例的模板有一些问题. 存在的问题: 只要继承了单例的模板就无法再继承其他的 ...

  6. Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介

    约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...

  7. Unity 游戏框架搭建 (十七) 静态扩展GameObject实现链式编程

    本篇本来是作为原来 优雅的QChain的第一篇的内容,但是QChain流产了,所以收录到了游戏框架搭建系列.本篇介绍如何实现GameObject的链式编程. 链式编程的实现技术之一是C#的静态扩展.静 ...

  8. Unity 游戏框架搭建 (十二) 简易AssetBundle打包工具(二)

    上篇文章中实现了基本的打包功能,在这篇我们来解决不同平台打AB包的问题. 本篇文章的核心api还是: BuildPipeline.BuildAssetBundles (outPath, 0, Edit ...

  9. Unity 游戏框架搭建 2019 (二十一、二十二) 第三章简介&整理前的准备

    整理前的准备 到目前为止,我们积攒了很多示例了,并且每个示例也都贯彻了最的约定和规则. 在上一篇的小结也说了一个比较新的东西:编程体验优化. 在之前我们还积攒了一个问题:代码重复问题. 我们可是忍住整 ...

随机推荐

  1. [翻译]Review——How to do Speech Recognition with Deep Learning

    原文地址:https://medium.com/@ageitgey/machine-learning-is-fun-part-6-how-to-do-speech-recognition-with-d ...

  2. mysql 查询近几天的结果 按每天的粒度查询

    ),DATE_FORMAT(FROM_UNIXTIME(createtime), '%Y-%m-%d') as time from bskuser group by time

  3. Js的核心:找到DOM

    掌握 JavaScript 的核心之一:DOM,能够熟悉DOM相关操作,了解JavaScript事件机制 一.使用getElementById().getElementsByTagName().chi ...

  4. html简介(自己理解和老师讲课)

    首先讲开发网页三种技术:html,css,javascript.html负责网页的结构,css站在没学角度对网页进行美化,javascript负责网页交互,站在用户体验角度设计网页交互效果 而我们所学 ...

  5. C# 修改GroupBox的边框颜色和字体颜色

    改变GroupBox边框和的颜色 private void groupBox_BasicInformation_Paint(object sender, PaintEventArgs e) { e.G ...

  6. JAVA语法基础要点

  7. 图的存储结构(邻接矩阵与邻接表)及其C++实现

    一.图的定义 图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为: G=(V,E) 其中:G表示一个图,V是图G中顶点的集合,E是图G中顶点之间边的集合. 注: 在线性表中,元素个数可以为零, ...

  8. ORACLE_TO_CHAR Function

    TECHONTHENNTE  WEBSITE: https://www.techonthenet.com/oracle/functions/to_char.php Oracle / PLSQL: TO ...

  9. ZT linux 线程私有数据之 一键多值技术

    这个原作者的这个地方写错了 且他举的例子非常不好.最后有我的修正版本 pthread_setspecific(key, (void *)&my_errno); linux 线程私有数据之一键多 ...

  10. 如何制作EDM邮件营销模板之图片注意事项

    在制作EDM邮件营销的邮件模板的时候我们总喜欢添加一些图片来提高读者阅读兴趣,现在U-Mail邮件群发平台根据已有的一些经验来分享给一下邮件内容中添加图片要注意的问题: 1.尽量少使用图片,特别是重要 ...