当前位置: 首页 > news >正文

【Lua热更新】下篇

上篇链接:【Lua热更新】上篇

文章目录

    • 三、`xLua`热更新
      • 📖1.概述
      • 📚︎2.导入`xLua`框架
      • 🔖3. C#调用`Lua`
        • 3.1`Lua`解析器
        • 3.2`Lua`文件夹的重定向
        • 3.3`Lua`解析器管理器
        • 3.4全局变量获取
        • 3.5全局函数获取
        • 3.6映射到List和Dictionary
        • 3.7映射到类
        • 3.8映射到接口
        • 3.9映射到`LuaTable`
      • 🔖4.`Lua`调用C#
        • 1.`Lua`使用C#类
        • 2.`Lua`使用C#枚举
        • 3.`Lua`使用C#数组、List、字典
        • 4.`Lua`使用C#拓展方法
        • 5.`Lua`使用C#`ref`和`out`
        • 6.`Lua`使用C#重载函数
        • 7.`Lua`使用C#委托和事件
        • 8.`Lua`使用C#二维数组
        • 9.`Lua`使用C#的null和nil的比较
        • 10.`Lua`和系统类或委托相互使用
        • 11.`Lua`使用C#协程
        • 12.`Lua`使用C#泛型函数
      • 🔖5.`xLua`热补丁

三、xLua热更新

📖1.概述

C#代码和Resoureces文件夹不能实现热更新,所以需要使用Lua通过AB包来进行更新脚本

在这里插入图片描述

📚︎2.导入xLua框架

🔹1.获得xLua包,GitHub地址:Tencent/xLua: xLua is a lua programming solution for C#

🔹2.导入文件夹

在这里插入图片描述

增加了xLua编辑栏导入成功

🔹3.点击Generate Code生成相关代码

在这里插入图片描述

🔹4.导入AssetBundles-Browser

GitHub地址:Unity-Technologies/ AssetBundles -Browser

如果有报错删除文件中的报错的Test文件即可

🔖3. C#调用Lua

3.1Lua解析器

Lua解析器让我们在Unity中执行Lua

🎒简述一下原理,就是Lua是一种解释型语言,代码在运行时才被解释器一行行动态翻译和执行,先由Lua编译器编译为字节码,然后交给Lua虚拟机执行,将Lua虚拟机源代码整合给Unity使用实现之间的交互

// 创建一个解析器
LuaEnv luaEnv = new LuaEnv();
// 使用Lua解析器执行Lua语言
luaEnv.DoString("print('First Lua')");
// Lua垃圾回收,手动释放对象
luaEnv.Tick();
// 销毁Lua解析器
luaEnv.Dispose();

🔹Lua默认调用Resources文件夹下的Lua脚本

执行以下代码会发现脚本报错没有找到脚本,原因是因为.lua不是Unity中的文件格式,所以我们需要给.lua在添加一个.txt后缀才能查找到,这个查找函数的源码估计是通过Resourece.Load加载,所以查找不到Lua文件

luaEnv.DoString("require('FistTest')");

在这里插入图片描述

❓存在的问题,从Resources文件夹下读取无法进行热更新,其次每个文件都要手动添加.txt后缀十分麻烦,所以后续需要进行文件夹的重定向和自动

3.2Lua文件夹的重定向

通过AddLoader方法进行重定向,进行自定义加载Lua文件规则

void Start()
{luaEnv.AddLoader(MyCustomLoader);luaEnv.DoString("require('FistTest')");
}private byte[] MyCustomLoader(ref string path)
{string newPath = Application.dataPath + "/Lua/" + path + ".lua";if (File.Exists(newPath))return File.ReadAllBytes(newPath);elseDebug.Log("重定向失败" + path);return null;
}

🎒我们看一下AddLoader源码

传入一个委托添加到 List 列表中,在我们使用require(文件名)时如果在列表中按顺序查找该文件,那么最后将在Resoureces文件夹中查找

在这里插入图片描述

❓存在问题,最终我们需要在AB包中进行加载

3.3Lua解析器管理器

🔹通过实现Lua解析器管理器保证解析器的唯一性

👓️LuaMgr管理器源码

public class LuaMgr : BaseManager<LuaMgr>
{private const string LUA_PATH = "Lua";private LuaEnv luaEnv;public LuaTable Global {get => luaEnv.Global;} // 获得_G表// 初始化解析器public void Init(){if (luaEnv != null) return;luaEnv = new LuaEnv();luaEnv.AddLoader(MyCustomLoader); // 后缀.lua时日常开发重定向到的位置luaEnv.AddLoader(MyCustomABLoader); // 后缀.txt使用AB包时重定向到的位置}// 重定向加载AB包中的Lua脚本private byte[] MyCustomABLoader(ref string path){TextAsset txtLua = ABMgr.GetInstance().LoadRes<TextAsset>(LUA_PATH, path + ".lua");if (txtLua == null){Debug.LogError("在AB包中重定向失败");return null;}return txtLua.bytes;}private byte[] MyCustomLoader(ref string path){string newPath = Application.dataPath + "/"+ LUA_PATH + "/" + path + ".lua";if (File.Exists(newPath)){return File.ReadAllBytes(newPath);}else{Debug.LogError("重定向失败" + path);}return null;}// 执行lua语言public void DoString(string filename){if (luaEnv == null){Debug.LogError("解析器未初始化");return;}luaEnv.DoString(string.Format("require('{0}')", filename));}// 释放垃圾public void Tick(){if (luaEnv == null){Debug.LogError("解析器未初始化");return;}luaEnv.Tick();}// 销毁解析器public void Dispose(){luaEnv.Dispose();luaEnv = null;}
}

👓️BaseManager 源码

//1.C#中 泛型的知识
//2.设计模式中 单例模式的知识
public class BaseManager<T> where T:new()
{private static T instance;public static T GetInstance(){if (instance == null)instance = new T();return instance;}
}

❓现在只实现了重定向文件夹,并没有实现重定向AB包加载,接下来继续完成将Lua脚本放在AB包中,通过AB包加载脚本。需要注意的是AB包加载文本后缀不能使用.Lua,所以在打包时需要将后缀改为.txt,不过这一步后续将改成自动的

🔹我们将后缀暂时手动的修改后缀为.txt,进行进行打包,如果打包过程中存在报错,XLua → Clear Generated Code即可

在这里插入图片描述

打包成功后刷新界面

在这里插入图片描述

重定向加载AB包

🔹这里建议实际开发中仍然使用.Lua后缀,那么将重定向读取普通文件夹,进行热更新时在修改为.txt后缀重定向到AB包文件夹

// 重定向加载AB包中的Lua脚本
private byte[] MyCustomABLoader(ref string path)
{string newPath = Application.streamingAssetsPath + "/"+ LUA_PATH;AssetBundle ab = AssetBundle.LoadFromFile(newPath); // 加载AB包TextAsset tx = ab.LoadAsset<TextAsset>(path + ".lua"); // 加载Lua文件return tx.bytes;
} 	
3.4全局变量获取

👓️Main.Lua源码

print("=== Main ===")
require('FirstTest') -- 在Unity中已经重定向

👓️FirstTest.Lua 源码

print("=== First Test ===")
myNumber = 1
myFloat = 1.2
myBool = true
myString = "OvO"

👓️Unity中代码

🔹无法直接获得local本地变量,因为不在_G表中

LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoString("Main");// Get
int myNumber = LuaMgr.GetInstance().Global.Get<int>("myNumber");
float myFloat = LuaMgr.GetInstance().Global.Get<float>("myFloat");
bool myBool = LuaMgr.GetInstance().Global.Get<bool>("myBool");
string myString = LuaMgr.GetInstance().Global.Get<string>("myString");// Set
LuaMgr.GetInstance().Global.Set("myNumber", 99);
myNumber = LuaMgr.GetInstance().Global.Get<int>("myNumber");
3.5全局函数获取

👓️FirstTest.Lua 源码

myFun1 = function ()print("这是一个无参无返回的函数")
endmyFun2 = function (x)print("这是一个有参有返回的函数")return x + x
endmyFun3 = function ()print("这是一个多返回值的函数")return 99, "apple", 1.2f
endmyFun4 = function (x, ...)print("这是一个变长参数的函数")arg = {...}for k, v in pairs(arg) doprint(k, v)end
end

🔹无参数无返回函数获取

// Unity自带委托
UnityAction ua1 = LuaMgr.GetInstance().Global.Get<UnityAction>("myFun1");
ua1.Invoke();
// C#委托
Action ac = LuaMgr.GetInstance().Global.Get<Action>("myFun1");
// xLua提供:不建议使用
LuaFunction lf = LuaMgr.GetInstance().Global.Get<LuaFunction>("myFun1");
lf.Call();

🔹有参数有返回函数获取

定义一个委托,需要使用特性否则Lua无法识别,XLua → Generate Code在编译器重新生成一下代码否则也会报错

[CSharpCallLua]
public delegate int MyFunc2(int x);

调用函数

MyFunc2 ua2 = LuaMgr.GetInstance().Global.Get<myFunc2>("myFun2");
int x = ua2.Invoke(2);
print("返回值是" + x);

🔸或者使用C#封装的委托,如果报错就XLua → Generate Code

Func<int, int> ua2 = LuaMgr.GetInstance().Global.Get<Func<int, int>>("myFun2");
int x = ua2.Invoke(2);
print("返回值是" + x);

🔹多返回值函数获取

// 第一个返回值即委托返回值
// 之后的返回值使用out或者ref
[CSharpCallLua]
public delegate int MyFun3(int x, out string fruit, out float value);

调用

MyFun3 fc3 = LuaMgr.GetInstance().Global.Get<MyFun3>("myFun3");
string fruit;
float value;
int a = fc3.Invoke(12, out fruit, out value);

或者使用xLua自带的获取方法

LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("myFun3");
object[] ojb = lf3.Call(100);

🔹多返回值函数获取

// 如果变长参数每个类型都不确定传入Object
[CSharpCallLua]
public delegate void MyFun4(string a, params int[] args);
3.6映射到List和Dictionary

👓️FirstTest.Lua 源码

testList = {1, 2, 3, 4}
testDic = {["a"] = 1,["b"] = 2,["c"] = 3
}

获取

LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");List<int> testList = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
Dictionary<string, int> testDic = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
3.7映射到类

👓️FirstTest.Lua 源码

testClass = {name = "咪咪",age = 1,eat = function ()print("吃猫粮")end
}

🔹C#中声明该类,成员名称必须和lua中一致,类名可以不需要一致,成员必须是公共的

// 少于lua中的变量则获取不到,多余lua中的变量则不会赋值,并不会报错
public class TestClass
{public string name;public int age;public UnityAction eat;
}
3.8映射到接口

🔹声明接口,接口中不允许声明成员变量,所以使用成员属性来接收

🔹接口拷贝是引用拷贝,在C#中修改值Lua中也会被修改

[CSharpCallLua] // 需要特性
public interface MyInterface
{public string name { get; set; }public int age { get; set; }public UnityAction eat{ get; set; }
}

获取

MyInterface myInterface = LuaMgr.GetInstance().Global.Get<MyInterface>("TestClass");
3.9映射到LuaTable

官方不建议使用LuaTableLuaFunction会有垃圾占用,LuaTable需要手动销毁否则会一致占用内存

LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("TestClass");
// 获得
string name = table.Get<string>("name");
int age = table.Get<int>("age");
UnityAction ac = table.Get<UnityAction>("eat");// 修改
table.Set("age", 55);
// 释放table垃圾
table.Dispose();

🔖4.Lua调用C#

1.Lua使用C#类

🔹C#创建Lua解析器,调用Lua脚本运行

LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");

🔹Lua调用C#类需要使用完整路径,CS.命名空间.类名

Lua中创建对象不需要使用new,调用构造函数即可

local obj1 = CS.UnityEngine.GameObject()

🔹为了方便使用和节约性能,定义全局变量存储C#中的类

GameObject = CS.UnityEngine.GameObject
local obj2 = GameObject("MyObject") -- 调用有参构造函数

🔹可以直接使用类中的静态方法

local obj3 = GameObject.Find("MyObject")

🔹对象可以调用成员变量和成员方法

​ 🔸需要注意的是成员方法需要使用冒号调用

-- 得到对象成员变量
print(obj3.transform.position) 
-- Lua中调用对象的成员方法要使用冒号调用
Vector3 = CS.UnityEngine.Vector3
obj3.transform:Translate(Vector3.forward)

🔹Lua中调用一个自定义类(不继承MonoBehaviour)

namespace MySpace // 命名空间
{public class MyClass // 类名{public string value;public void Speak(string str) { Debug.Log(str); }}
}
local myClass = CS.MySpace.MyClass()
myClass:Speak("调用了一个自定义C#类")

🔹为物体添加组件(即获得继承MonoBehaviour类)

public class StuLua : MonoBehaviour
{void Start(){Debug.Log("添加成功");}
}
local obj = GameObject.Find("MyObject")
obj:AddComponent(typeof(CS.StuLua)) -- xLua不支持无参泛型,xLua可以通过typeof获得泛型
2.Lua使用C#枚举

🔹枚举调用和类调用是类似的:CS.命名空间.枚举名.枚举成员

​ 我们也可以调用自定义枚举,使用方法相同所以略过

GameObject = CS.UnityEngine.GameObject
PrimitiveType = CS.UnityEngine.PrimitiveTypelocal cube = GameObject.CreatePrimitive(PrimitiveType.Cube)

🔹数值转、字符串转枚举:__CastFrom

local eValue = PrimitiveType.__CastFrom(1) -- 数值
print(eValue)
GameObject.CreatePrimitive(eValue)evalue = PrimitiveType.__CastFrom("Sphere") -- 字符串
print(evalue)
GameObject.CreatePrimitive(evalue)
3.Lua使用C#数组、List、字典

🔹数组

local myClass = CS.MySpace.MyClass()-- 长度获取要按照c#中使用,不能用lua中的#获取长度
print(myClass.myIntArray.Length) -- 获得长度
print(myClass.myIntArray[0]) -- 获得元素
for i = 0, myClass.myIntArray.Length - 1 doprint(myClass.myIntArray[i])
end

Lua中创建C#数组,数组的本质是Array我们通过Array类来创建数组

local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32), 10)

🔹List

myClass.myIntList:Add(1)
myClass.myIntList:Add(2)
print(myClass.myIntList.Count)
for i = 0, myClass.myIntList.Count - 1 doprint(myClass.myIntList[i])
end

Lua中创建C#List,通过xLua中提供的方法来创建,需要两步先获得list泛型类,在创建对象

-- 得到一个List<string>的类
local List_String = CS.System.Collections.List(CS.System.String)
-- 创建一个List<string>对象
local list = List_String()
list:Add("apple")

🔹字典

myClass.myDict:Add(1, "orange")
print(myClass.myDict[1])
for k,v in pairs(myClass.myDict) doprint(k, v)
end

Lua中创建C# Dictionary

-- 得到一个Dictionary<String, Vector3>的类
local dic_str_vec3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3)
-- 创建一个Dictionary<String, Vector3>对象
local dic = dic_str_vec3()dic:Add("up", CS.UnityEngine.Vector3.up)
for k, v in pairs(dic) doprint(k, v)
end

Lua中自己创建的字典无法通过[]获得,需要使用方法get_Item、set_Item

print(dic["up"]) -- 这个是错误的
print(dic:get_Item("up")) -- 需要使用方法
dic:set_Item("up", nil) -- 需要使用方法
print(dic:get_Item("up"))
4.Lua使用C#拓展方法

🔹需要使用特性[LuaCallCSharp],另外如果是Lua中使用的类都建议加上特性,可以提升性能否则Lua都是通过反射调用的效率比较低

C#代码:

[LuaCallCSharp]
public static class Tool
{public static void Func(this MyClass obj){Debug.Log("这是一个MyClass的拓展方法" + obj.ToString());}
}

Lua代码

MyClass = CS.MySpace.MyClass
local my = MyClass()
my:Func();
5.Lua使用C#refout
public class MyClass
{public int RefFun(int a, ref int b){return a + b + 1;}public int OutFun(int a, out int b){b = a + 1;return a + b;}public int AllFun(int a, ref int b, out int c){c = a + b;return a + b + c;}
}

🔹refout都通过多返回值获得,ref必须传入参数,out参数省略

MyClass = CS.MySpace.MyClass
local my = MyClass()local back, b = my:RefFun(2, 3);
print(b)local back, b = my:OutFun(2)
print(b)local back, b, c = my:AllFun(2, 4 ,5)
pint(b)
pint(c)
6.Lua使用C#重载函数
public string Call(string a)
{Debug.Log(a);return a;
}
public string Call(string a, string b)
{Debug.Log(a + b);return a + b;
}
public void Call(int x)
{Debug.Log("int type function" + x);
}
public void Call(float x)
{Debug.Log("float type function" + x);
}

🔹对于参数个数不同的函数,虽然Lua本身不支持调用重载函数,都是Lua支持调用C#中的重载函数

my:Call("apple")
my:Call("apple", "orange")

🔹但是对于参数个数相同,类型不同的number类型时,Lua可能会出现一些问题,因为Lua数值类型只有number无法区分intfloat

所以在调用void Call(float x)void Call(int x)会有意想不到的结果,不建议这样使用,xLua有反射方法可以解决但是性能不高

7.Lua使用C#委托和事件
public UnityAction ac;
public event UnityAction ev;public void CallEvent() // 事件无法在类外调用
{ev?.Invoke();
}

🔹委托的使用

local func = function( )print("这是一个无参无返回的函数")
end-- 第一次赋值必须使用 = ,不能使用+=
my.ac = func
-- 添加函数
my.ac = my.ac + func
-- 调用委托
print("=====add====")
my.ac()-- 删除委托
my.ac = my.ac - func
print("=====del====")
my.ac()-- 清空委托
my.ac  = nil

🔹事件的使用,Lua中使用事件比较特殊,语法类似成员函数调用,事件的调用和清空无法在外部直接控制,只能通过类方法包装后在外部使用方法

print("===事件====")
-- 添加函数
my:ev("+", func)
my:ev("+", func)
-- 调用事件,通过成员方法,因为事件无法在类外被调用
my:CallEvent()print("=== after ===")
-- 删除函数
my:ev("-", func)
my:CallEvent()
8.Lua使用C#二维数组
public int[,] array = new int [,] { { 1, 2, 3 }, { 4, 5, 6 } };

🔹主要是通过Array中封装的方法去使用

-- 获取行数列数
-- GetLength是C#中的方法,传递0表示行数,传递1表示列数
print("行数 : " .. my.array:GetLength(0))
print("列数 : " .. my.array:GetLength(1))-- 获取元素,不能通过[][]或者[,]获得,通过Array中的方法使用
print("(1, 1) : " .. my.array:GetValue(1, 1))-- 遍历
for i = 0, my.array:GetLength(0) - 1 dofor j = 0, my.array:GetLength(1) - 1 doprint(i..j.." : "..my.array:GetValue(i, j))end
end
9.Lua使用C#的null和nil的比较

🔸判断物体上是否有刚体组件如果没有则添加刚体

🔹nilnull无法进行==相等比较

GameObject = CS.UnityEngine.GameObject
Rigidbody = CS.UnityEngine.Rigidbody-- 创建对象
local obj = GameObject("test object")
-- Lua中无法使用泛型,所以我们使用参数为type的方法
local rig = obj:GetComponent(typeof(Rigidbody))if rig == nil then -- 这样是错误的无法判断print("添加一个rigibody")obj:AddComponent(typeof(Rigidbody))
elseprint("rigibody已经存在")
end

🔹我们可以通过Equals方法来判断,所以我们将 rig == nilrig:Equals(nil)

🔹但是可能对象本身就是空的所以,通常我们会将判空封装成一个方法 rig:Equals(nil)IsNull(rig)

function IsNull(obj)if obj == nil or obj:Equals(nil) thenreturn trueendreturn false
end

🔹或者我们在C#中为Object类添加拓展方法来判断,拓展方法记得添加特性和重新生成Lua代码

[LuaCallCSharp]
public static class Tool
{public static bool IsNull(this Object obj){return obj.Equals(null);}
}
10.Lua和系统类或委托相互使用

🔹CSharpCallLua当c#中调用Lua委托或接口时使用

🔹LuaCallCSharpLua中调用C#拓展方法时使用,建议被Lua调用的类都添加可以提高性能效率

❓我们无法为系统类或者第三方库代码加上这两个特性

🔸通过固定方法为系统类和自定义类统一添加特性

public static class CreateAttribute
{[CSharpCallLua] public static List<Type> csharpCallLuaList = new List<Type>(){typeof(UnityAction),typeof(MyClass)};[LuaCallCSharp] public static List<Type> luaCallCSharpList = new List<Type>(){typeof(GameObject),typeof(MyClass)};
}
11.Lua使用C#协程

🔹Lua中不能直接使用 yield return

🔹lua中不能直接传入lua函数,所以需要先调用xlua.util中的cs_generator(lua函数)

util = require("xlua.util")
GameObject = CS.UnityEngine.GameObject
WaitForSeconds = CS.UnityEngine.WaitForSecondslocal obj = GameObject("myObject")
local comp = obj:AddComponent(typeof(CS.StuLua))fun = function ()local x = 1while true docoroutine.yield(WaitForSeconds(1)) -- Lua中不能直接使用 yield return print(x)x = x + 1if x > 10 thencomp:StopCoroutine(myIE) -- 停止协程和C#中一样endend
end-- lua中不能直接传入lua函数,所以需要先调用xlua.util中的cs_generator(lua函数)
myIE = comp:StartCoroutine(util.cs_generator(fun))
12.Lua使用C#泛型函数

🔹lua中支持有约束有参数的泛型

🔹lua中不支持没有约束、没有参数、约束为接口的泛型函数

public void Func<T>(T a) where T : Enum
{Debug.Log("这是一个有参数有约束的方法");
}
MyClass = CS.MySpace.MyClass
local my = MyClass()my:Func(1)

🔖5.xLua热补丁

相关文章:

【Lua热更新】下篇

上篇链接&#xff1a;【Lua热更新】上篇 文章目录 三、xLua热更新&#x1f4d6;1.概述&#x1f4da;︎2.导入xLua框架&#x1f516;3. C#调用Lua3.1Lua解析器3.2Lua文件夹的重定向3.3Lua解析器管理器3.4全局变量获取3.5全局函数获取3.6映射到List和Dictionary3.7映射到类3.8映…...

Facebook 与数字社交的未来走向

随着数字技术的飞速发展&#xff0c;社交平台的角色和形式也在不断演变。作为全球最大社交平台之一&#xff0c;Facebook&#xff08;现Meta&#xff09;在推动数字社交的进程中扮演了至关重要的角色。然而&#xff0c;随着互联网的去中心化趋势和新技术的崛起&#xff0c;Face…...

微信小程序实现二维码海报保存分享功能

首先在写这个二维码分享海报的时候试过很多方法&#xff0c;比如&#xff1a;canvas中的这个createCanvasContext创建上下文的方法&#xff0c;去网上一搜就是一大堆&#xff0c;但其实这个方法已经被废弃了。Canvas 实例&#xff0c;可通过 SelectorQuery 获取。这是绘制背景图…...

Android 搭建AIDL Client和Server端,双向通信

一、背景 使用AIDL,搭建Client和Server端,实现跨进程通讯,即两个应用之间可以相互通讯。这里列举AIDL实现的方式和需注意的细节&#xff0c;并附上源码。 二、实现方式 2.1 定义AIDL需要的接口,名字为xxx.aidl,Client和Server端 AIDL接口的包名和aidl文件必须一致&#xff0c…...

深度学习从入门到精通——图像分割实战DeeplabV3

DeeplabV3算法 参数配置关于数据集的配置训练集参数 数据预处理模块DataSet构建模块测试一下数据集去正则化模型加载模块DeepLABV3 参数配置 关于数据集的配置 parser argparse.ArgumentParser()# Datset Optionsparser.add_argument("--data_root", typestr, defa…...

STM32-笔记5-按键点灯(中断方法)

1、复制03-流水灯项目&#xff0c;重命名06-按键点灯&#xff08;中断法&#xff09; 在\Drivers\BSP目录下创建一个文件夹exti&#xff0c;在该文件夹下&#xff0c;创建两个文件exti.c和exti.h文件&#xff0c;并且把这两个文件加载到项目中&#xff0c;打开项目工程文件 加载…...

C++ 只出现一次的数字 - 力扣(LeetCode)

点击链接即可查看题目&#xff1a;136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间…...

C++设计模式:享元模式 (附文字处理系统中的字符对象案例)

什么是享元模式&#xff1f; 享元模式是一个非常实用的结构型设计模式&#xff0c;它的主要目的是节省内存&#xff0c;尤其在需要创建大量相似对象时。 通俗解释&#xff1a; 想象我们在写一本书&#xff0c;每个字母都需要表示出来。如果每个字母都单独用对象表示&#xff…...

android EditText密码自动填充适配

android上的密码&#xff08;其实不仅仅是密码&#xff0c;可以是用户名也可以是邮箱&#xff09;自动填充&#xff0c;是需要考虑适配的。 官方文档&#xff1a;https://developer.android.com/identity/autofill/autofill-optimize?hlzh-cn 什么是自动填充 手机厂商一般会…...

LeetCode 刷题笔记

LeetCode 刷题笔记 1. 20241218 &#xff08;1&#xff09;2447 std::gcd是C17引入的一个函数&#xff0c;用于计算两个整数的最大公因数。位于<numeric>头文件中。 #include <iostream> #include <numeric> // std::gcdint main() {int a 36;int b 60…...

【Java基础面试题034】Java泛型擦除是什么?

回答重点 泛型擦除指的是Java编译器在编译时将所有泛型信息删除的过程&#xff0c;以确保与Java1.4及之前的版本保持兼容 泛型参数在运行时会被替换为其上界&#xff08;通常是Object&#xff09;&#xff0c;这样一来在运行时无法获取泛型的实际类型。 作用&#xff1a;泛型…...

使用ssh命令远程登录服务器的两种便捷方式:简化ssh命令、创建bat文件

1. 简化ssh命令 使用记事本打开该路径C:\Users\<你的用户名>\.ssh\下的config文件&#xff0c;粘贴以下代码&#xff1a; Host myserverHostName 192.168.1.1(这里换成你的ip地址)User your_username(这里换成你的用户名)Port 22保存文件后现在在cmd中直接输入ssh myserv…...

access数据库代做/mysql代做/Sql server数据库代做辅导设计服务

针对Access数据库、MySQL以及SQL Server数据库的代做和辅导设计服务&#xff0c;以下是一些关键信息和建议&#xff1a; 一、服务概述 这些服务通常包括数据库的设计、创建、优化、维护以及相关的编程和查询编写等。无论是Access这样的桌面关系数据库管理系统&#xff08;RDB…...

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题

第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书-A 目录 一、竞赛阶段 二、竞赛任务书内容 &#xff08;一&#xff09;拓扑图 &#xff08;二&#xff09;模块A 基础设施设置与安全加固(200分) &#xff08;三&#xff09;B模块安全事件响应/网络安全数据取证/…...

Android学习(五)-Kotlin编程语言-面向对象中的 继承-构造函数-接口三模块学习

首先&#xff0c;我们需要定义一个 Person 类&#xff1a; open class Person {var name ""var age 0fun eat() {println("$name is eating.")} } 注意&#xff0c;Person 类前面加上了 open 关键字&#xff0c;表示这个类可以被继承。在 Kotlin 中&am…...

滑动窗口 + 算法复习

维护一个满足条件的窗口大小&#xff0c;然后进行双指针移动 1.最长子串 题目链接&#xff1a;1.最长子串 - 蓝桥云课 #include<bits/stdc.h> #define int long long using namespace std; string s; int k; signed main() {int max_len0,left0;cin>>s>>k;…...

贪心算法 greedy

文章目录 参考贪心算法[Leetcode455 分发饼干](https://leetcode.cn/problems/assign-cookies/description/)分析题解 [Leetcode135 分发糖果](https://leetcode.cn/problems/assign-cookies/description/)分析题解 leetcode435无重叠区间分析题解 参考 https://github.com/ch…...

基于python的家教预约网站-家教信息平台系统

标题:基于 Python 的家教预约网站-家教信息平台系统 内容:1.摘要 本文介绍了一个基于 Python 的家教预约网站-家教信息平台系统。该系统旨在为学生和家长提供一个方便、高效的家教预约平台&#xff0c;同时也为家教老师提供一个展示自己教学能力和经验的机会。本文详细介绍了系…...

基于深度学习多图像融合的屏幕缺陷检测方案

公司项目&#xff0c;已申请专利。 深度学习作为新兴技术在图像领域蓬勃发展&#xff0c;因其自主学习图像数据特征的性能避免了人工设计算法的繁琐&#xff0c;精准的检测性能、高效的检测效率以及对各种不同类型的图像任务都有比较好的泛化性能&#xff0c;使得深度学习技术在…...

MySQL基础笔记(三)

在此特别感谢尚硅谷-康师傅的MySQL精品教程 获取更好的阅读体验请前往我的博客主站! 如果本文对你的学习有帮助&#xff0c;请多多点赞、评论、收藏&#xff0c;你们的反馈是我更新最大的动力&#xff01; 创建和管理表 1. 基础知识 1.1 一条数据存储的过程 存储数据是处理数…...

【JetPack】WorkManager笔记

WorkManager简介&#xff1a; WorkManager 是 Android Jetpack 库中的一个重要组件。它用于处理那些需要在后台可靠执行的任务&#xff0c;这些任务可以是一次性的&#xff0c;也可以是周期性的&#xff0c;甚至是需要满足特定条件才执行的任务。例如&#xff0c;它可以用于在后…...

docker 安装 ftp

前言 经多次测试 不知道为什么 必须添加被动模式跟端口才可以 连接成功&#xff0c;有知道为什么可以评论下 下载镜像 docker pull fauria/vsftpd启动ftp 服务 参考链接 docker run -d -v /etc/localtime:/etc/localtime:ro -v /home/dr/data/ftp:/home/vsftpd \ -e "…...

5.C语言内存分区-堆-栈

目录 内存分区 运行之前 代码区 全局初始化数据区 、静态数据区 (data) 未初始化数据区(bss&#xff08;Block Started by Symbol&#xff09;区) 总结 运行之后 代码区 &#xff08;text segment&#xff09; 未初始化数据区(bss) 全局初始化数据区&#xff0c;静态…...

传统CV算法——基于opencv的答题卡识别判卷系统

基于OpenCV的答题卡识别系统&#xff0c;其主要功能是自动读取并评分答题卡上的选择题答案。系统通过图像处理和计算机视觉技术&#xff0c;自动化地完成了从读取图像到输出成绩的整个流程。下面是该系统的主要步骤和实现细节的概述&#xff1a; 1. 导入必要的库 系统首先导入…...

国产 HighGo 数据库企业版安装与配置指南

国产 HighGo 数据库企业版安装与配置指南 1. 下载安装包 访问 HighGo 官方网站&#xff08;https://www.highgo.com/&#xff09;&#xff0c;选择并下载企业版安装包。 2. 上传安装包到服务器 将下载的安装包上传至服务器&#xff0c;并执行以下命令&#xff1a; [rootmas…...

「Mac畅玩鸿蒙与硬件46」UI互动应用篇23 - 自定义天气预报组件

本篇将带你实现一个自定义天气预报组件。用户可以通过选择不同城市来获取相应的天气信息&#xff0c;页面会显示当前城市的天气图标、温度及天气描述。这一功能适合用于动态展示天气信息的小型应用。 关键词 UI互动应用天气预报数据绑定动态展示状态管理 一、功能说明 自定义…...

Springboot @Transactional使用时需注意的几个问题

一、事务的隔离级别 在Springboot应用中&#xff0c;如果我们想实现方法一旦执行有异常产生&#xff0c;就触发事务回滚&#xff0c;可以在方法上面添加Transactional注解。如果应用采用mysql数据库&#xff0c;虽然mysql本身也有事务隔离机制&#xff0c;但在Sping数据库的应…...

数字经济下的 AR 眼镜

目录 1. &#x1f4c2; AR 眼镜发展历史 1.1 AR 眼镜相关概念 1.2 市面主流 XR 眼镜 1.3 AR 眼镜大事记 1.4 国内外 XR 眼镜 1.5 国内 AR 眼镜四小龙 2. &#x1f531; 关键技术 2.1 AR 眼镜近眼显示原理 2.2 AR 眼镜关键技术 2.3 AR 眼镜技术难点 3. &#x1f4a…...

力扣150题

88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 **注意&#xff1a;**…...

剑指offer搜索二维矩阵

题目连接 https://leetcode.cn/problems/search-a-2d-matrix-ii/’ 代码 自己想出来的 解法一 初始化两个指针&#xff0c;i0,j列数-1 若此时matrix[i][j]target 则返回true 若此时matrix[i][j]>target,表明在第j列中不可能存在target&#xff0c;因为列是升序的 若此时ma…...