
如果问你sizeof(MyStruct)=?,你可能会马上做个加法得到答案,但是答案不一定对。It depends! 假设我们是按照4个字节对齐,这上面的结构体在内存中实际排列如下图: 了解这个对于我们编码有两个意义: 1、通过合理排列字段声明顺序来优化存储效率,内存布局中不留空洞; 2、MarshalAsAttribute支持Layout.Explicit来进行绝对定位,懂得了字节对齐可以配合Unmanaged侧的内存排列规则以保证字段长度映射正确,不然同样会发生字段长度不一致带来的困扰。 Standard Marshalling Service/Interop marshaller总是试图释放Unmanaged侧代码分配的内存9,这会带来Double Free的问题,如果碰到这种问题,程序就会直接崩溃。 引用资料中举了以下例子: BSTR MethodOne (BSTR b) { return b; } 如果这段代码直接从Unmanaged侧DLL中直接执行,不会发生任何额外的内存释放;但是当你从Managed侧调用这个方法时,b会被释放两次。 而更让人抓狂的是,并没有相应的信息提示究竟是哪个指针,哪个字段被Double Free了,你唯一能做的就是一点点加代码来验证自己猜测。所以,严格来说,并没有一个万无一失的方案来避免Double Free,你唯一能做的就是通过测试来验证结果(有点盲拧魔方的味道了)。 有两个基本的方法来解决Double Free的问题: 1、按照官方文档建议,在Unmanaged侧通过使用CoTaskMemAlloc来分配内存,通过此种方法分配的内存,除非显式调用了CoTaskMemFree方法(在Unmanaged侧或者Managed侧均可以调用),Interop Marshaller会严格保证不去释放该内存。使用这种方法可以灵活的在任意一侧分配内存,并在合适的时候在另一侧释放内存。 2、但上面这种方法貌似仅适用于Windows平台,在macOS下没有办法使用(需要引用win32base.dll相关实现)。在macOS下仅能通过在Mananged侧调用Marshal.AllocCoTaskMem()方法分配内存,并通过Marshal.FreeCoTaskMem()来在同一侧进行释放(按照此方法分配的内存指针传入Unmanaged侧后,不要进行任何释放即可)。另外有一个不太可靠的workaround是:在Unmanaged一侧创建的内存指针尽量通过IntPtr传递,并在可能的时候将对象中一些指针类型的属性值置空,以避免Double Free的发生。 vptr和vtable是C++的一个概念:当你定义的类型中有虚函数存在时,内存对象的第一个位置会存放一个vptr指针,该指针指向vtable(虚函数表)。因此当你开始创建的自定义类型一开始没有虚函数时(包括虚析构函数virtual ~MyClass()),一切运行正常。有一天你重构此类型,增加了一些虚函数:DUANG,一切都崩塌了!原因就在于Unmanaged侧内存对象的排列规则变了,原有的对象字段都被新加入的vptr往后面移位了。此时可能你唯一能做的就是通过Layout.Explicit来手工对齐每一个字段新的位置。 坑一:针对M1芯片编译 对于M1芯片的macOS系统,编译环信IM Unity SDK时候需要注意几个问题: 1、XCode编译时需要Excluded Architecture中排除arm64架构(很奇葩的设置,不是应该排除x86吗?) 2、类库的依赖解决:通过otool -L命令来确认相应的plugin依赖的类库位置都正确(文件路径下文件确实存在),如果相应文件不存在要手工拷贝文件到指定目录:而新的macOS安全架构限制了往系统目录下(如/usr/lib)进行任何改动,一个临时的解决方法是通过install_name_tool工具主动修改类库依赖路径到另一个可以放置新文件的位置(如home目录)。 坑二:Delegate的正确使用姿势 如果Managed侧的编程语言是C#,则Delegate是实现回调的重要手段。在Unmanaged侧完成期望工作时回调一个FunctionPtr即可实现通用的回调模式,而此FunctionPtr正是对应到Managed侧的Delegate。当你的Delegate绑定到一个类对象上时,你有两种选择: namespace ChatSDK { //delegate definition public void delegate OnMessageReceived(EMMessage message); public class MyDelegate { //Option 1: field public OnMessageReceived MyMessageReceived; //Option 2: instance method public void OnMessageReceived(EMMessage message) { ... } } //send delegate method to unmanaged side MyDelegate md = new(); NativeMethods.SetOnMessageReceivedCallback(md.MyMessageReceived); //option 1 NativeMethods.SetOnMessageReceivedCallback(md.OnMessageReceived); //option 2 } 看起来两个方式都没有问题,并且第二个方式看起来更顺眼。但是这里隐藏着一个很深的坑,就是你选择第二个方式的时候,如果你在回调方法实现中采用this.xxx方式引用时,你会发现this = null!这是因为当你使用这种方式传递一个对象的方法作为回调方法指针时,其实已经丢失了Delegate.Target(也就是this)属性。而通过第一种方式传递的是一个对象的属性/字段,它和对象本身的绑定是不会在传递过程中丢失的。 至于该Delegate字段的定义可以在此类的构造函数中通过以下方式实现: ... public MyDelegate() { MyMessageReceived = (EMMessage message) => { ... } } ...
全新的F4依然保留着可模块化设计,这也是个人比较喜欢F4的地方,数字键区可以拆卸,也可以左右两边都可以安装。特别是像我这种即需要办公又需要游戏用户。办公的时候把数字键盘装上,游戏的时候又可以把数字键拆卸,非常方便。
(1)有几率在论剑、登剑阁(1V1和3V3)的五胜宝箱中额外开出魁罡悟元·太阳和魁罡悟元·太阳碎片;
格瑞丝冲向目标,使用加强型罪有应得技能攻击敌人。周围的队友会立刻获得一个持续朝向目标的单向圣光护盾,同时格瑞丝会获得一个全方位圣光护盾,助她抵御来自四面八方的伤害。圣光护盾将减少一部分的伤害。
PC Gamer在俄罗斯入侵乌克兰后自掉落的职位上报道说Burkatovskiy表示他支持“俄罗斯联邦武装部队的运作DPR [Donetsk人民共和国]和LPR [Luhansk人民共和国]。”
留言