ue4 NewObject/StaticConstructObject_Internal/StaticAllocateObject/FObjectInitializer:对象创建和初始化
UObject是一套很复杂的体系,之前读ue3代码时曾分析过其类型系统实现,主要是与UClass间的关系
现在转到ue4,发现那一块其实差不多,于是再重点备忘一下UObject本身的创建和初始化过程
1、首先,用NewObject<>来创建一个新对象:
template< class T >
T* NewObject(UObject* Outer, UClass* Class, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr)
{
if (Name == NAME_None)
{
FObjectInitializer::AssertIfInConstructor(Outer, TEXT("NewObject with empty name can't be used to create default subobjects (inside of UObject derived class constructor) as it produces inconsistent object names. Use ObjectInitializer.CreateDefaultSuobject<> instead."));
} #if DO_CHECK
// Class was specified explicitly, so needs to be validated
CheckIsClassChildOf_Internal(T::StaticClass(), Class);
#endif return static_cast<T*>(StaticConstructObject_Internal(Class, Outer, Name, Flags, EInternalObjectFlags::None, Template, bCopyTransientsFromClassDefaults, InInstanceGraph));
}
除了一些条件检测,直接调了以前老版的对应物StaticConstructObject_Internal。
这里的几个参数,有意思的是第5个参数Template,通过它可以复制对象。
2、StaticConstructObject_Internal,这里面主要做两件事:
一是用StaticAllocateObject来分配空间,
二是在此空间上初始化对象。
3、StaticAllocateObject:
首先是检查要创建新对象,还是替换一个原有对象,因为一路传进来的有个Name参数。如果找到一个已有的同名对象,那么会强制那个对象析构,然后重用它的空间,否则的话,就会直接分配空间了:
UObjectBase* FUObjectAllocator::AllocateUObject(int32 Size, int32 Alignment, bool bAllowPermanent)
除了这件主要的工作,还有不少其它的事,比如管理对象之间的连接、在异步线程中创建对象时的通知等等,暂时不深究了。
4、有了前者分配的空间,那么就可以在此处构造对象了:
Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, InternalSetFlags, bCanRecycleSubobjects, &bRecycledSubobject);
(*InClass->ClassConstructor)( FObjectInitializer(Result, InTemplate, bCopyTransientsFromClassDefaults, true, InInstanceGraph) );
这里ClassConstructor是每个UClass都有的一个函数指针类成员变量,实际上所有的UClass里该指针都指向一个全局模板函数:
template<class T>
void InternalConstructor( const FObjectInitializer& X )
{
T::__DefaultConstructor(X);
}
而每个类里的__DefaultConstructor也是用宏统一生成的,内容也是简单的转发给new:
static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())TClass(X); }
这个new的形式很不平凡,既有传给operator new的【(EInternal*)X.GetObj()】,又有传给该类实际构造函数的【FObjectInitializer& X】
后者正是InClass->ClassConstructor需要的实参,而前者也是通过宏(DECLARE_CLASS)定义在每个UObject里的一个operator new:
inline void* operator new( const size_t InSize, EInternal* InMem ) \
{ \
return (void*)InMem; \
}
总结一下上面绕来绕去的东西就是:
StaticAllocateObject分配了内存空间Result,然后以此为参数(当然还有其它的参数)构造了一个FObjectInitializer X,其中X.GetObj返回的就是这个内存地址Result;
接着用【 new(Result) TClass(X) 】来构造对象,指明地址在Result上,并将X做为参数传给其构造函数。
5、关于FObjectInitializer
接上步,在构造函数返回后,其临时参数【FObjectInitializer& X】是要自动析构的,因而ue4利用此步骤,在这里面做了很多事,大部份的事都是与该体系内部状态管理有关的,暂时难以透彻理解。
但有一个的操作与应用相关,即属性初始化,这是通过InitProperties来完成的:
void FObjectInitializer::InitProperties(UObject* Obj, UClass* DefaultsClass, UObject* DefaultData, bool bCopyTransientsFromClassDefaults)
{
……
UClass* Class = Obj->GetClass();
……if (!bNeedInitialize && bCanUsePostConstructLink)
{
// This is just a fast path for the below in the common case that we are not doing a duplicate or initializing a CDO and this is all native.
// We only do it if the DefaultData object is NOT a CDO of the object that's being initialized. CDO data is already initialized in the
// object's constructor.
if (DefaultData)
{
if (Class->GetDefaultObject(false) != DefaultData)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_FromTemplate);
for (UProperty* P = Class->PropertyLink; P; P = P->PropertyLinkNext)
{
P->CopyCompleteValue_InContainer(Obj, DefaultData);
}
}
else
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_InitProperties_ConfigEtcOnly);
// Copy all properties that require additional initialization (e.g. CPF_Config).
for (UProperty* P = Class->PostConstructLink; P; P = P->PostConstructLinkNext)
{
P->CopyCompleteValue_InContainer(Obj, DefaultData);
}
}
}
……
在这里通过遍历UClass上记录的属性元数据,可以对当前实例的每个属性进行赋值。
有趣的是DefaultData这个参数,也就是最早的Template参数,一路辗转到此。当然如果Template为空的话,这里的DefaultData就是该类的CDO了。
代码里明显体现出对此两种情况的不同处理策略:
如果是指定了要复制的对象Template->DefaultData,那么要遍历类上的所有的属性,因为对于一个实际的复制目标,你不知道它哪些属性已经改变了(不是默认值 ),因此必须全盘复制
而如果只是从CDO复制的话,那么只需要处理该类里明确指定过可能有初始化状态的字段,如打上CPF_Config标记的字段,它们在启动时会去ini文件里提取相应的配置值。
ue4 NewObject/StaticConstructObject_Internal/StaticAllocateObject/FObjectInitializer:对象创建和初始化的更多相关文章
- [你必须知道的.NET]第十九回:对象创建始末(下)
本文将介绍以下内容: 对象的创建过程 内存分配分析 内存布局研究 接上回[第十八回:对象创建始末(上)],继续对对象创建话题的讨论>>> 2.2 托管堆的内存分配机制 引用类型的实例 ...
- Java中对象创建过程
本文介绍的对象创建过程仅限于普通Java对象,不包括数组和Class对象. 1.类加载检查 虚拟机遇到一条new指令时,首先去检查该指令的参数能否在常量池中定位到一个类的符号引用,并且检查这个符号引用 ...
- Progress.js – 为页面上的任意对象创建进度条效果
Progress.js 是一个 JavaScript 和 CSS3 的库,它帮助开发人员为网页上的每个对象创建和管理进度条效果.你可以设计自己的模板,进度条或者干脆定制. 您可以使用 Progress ...
- 《JavaScript模式》第5章 对象创建模式
@by Ruth92(转载请注明出处) 第5章:对象创建模式 JavaScript 是一种简洁明了的语言,并没有其他语言中经常使用的一些特殊语法特征,如 命名空间.模块.包.私有属性 以及 静态成员 ...
- C#与数据库访问技术总结(六)之Command对象创建SQl语句代码示例
Command对象创建SQl语句代码示例 说明:前面介绍了 Command 对象的方法和一些属性,回顾一下 Command对象主要用来执行SQL语句.利用Command对象,可以查询数据和修改数据. ...
- javascript一种新的对象创建方式-Object.create()
1.Object.create() 是什么? Object.create(proto [, propertiesObject ]) 是E5中提出的一种新的对象创建方式,第一个参数是要继承的原型,如果不 ...
- 【JavaScript回顾】对象创建的几种模式
组合使用构造函数模式和原型模式 创建自定义类型的常见方式,就是组合使用构造函数模式与原型模式.构造函数模式用于定义实 例属性,而原型模式用于定义方法和共享的属性.结果,每个实例都会有自己的一份实例属性 ...
- Cocos2d-js中使用纹理对象创建Sprite对象
本节我们会通过一个实例介绍纹理对象创建Sprite对象使用,这个实例如图5-2所示,其中地面上的草是放在背景(如下图所示)中的,场景中的两棵树是从后图所示的“树”纹理图片中截取出来的,图5-5所示是树 ...
- 实例:使用纹理对象创建Sprite对象
精灵类是Sprite,它的类图如下图所示: Sprite类直接继承了Node类,具有Node基本特征.此外,我们还可以看到Sprite类的派生类有:PhysicsSprite和Skin.Physics ...
随机推荐
- Mac下命令行中用sublime打开指定文件 设置方法
sublime2版本 官网提供的方法:sublime2 官网提供的方法是:ln -s "/Applications/Sublime Text 2.app/Contents/SharedSup ...
- iOS中CocoaPads的安装与配置(总结)
来说一下安装CocoaPots的顺序: Xcode->homebrew->RVM->Ruby->CocoaPats; 这篇文章之讲解如何安装到Ruby的部分,关于安装好Ruby ...
- RDD:基于内存的集群计算容错抽象(转)
原文:http://shiyanjun.cn/archives/744.html 该论文来自Berkeley实验室,英文标题为:Resilient Distributed Datasets: A Fa ...
- java中获取接口(方法)中的参数名字(eclipse设置编译参数)(java8 javac -parameters)
interface接口参数 jdk1.7及以前使用spring功能实现的: 注意: 1.该功能只能获取类的方法的参数名,不能获取接口的方法的参数名. public static void test() ...
- make[2]: *** No rule to make target `/root/.pyenv/versions/anaconda3-2.4.0/lib/libpython3.5m.so', needed by `evaluation.so'. Stop.
当出现No rule to make target ,肯定是Makefile有问题. 有的makefile是脚本生成的,你得看脚本的配置文件对不对. 我的是这个脚本生成的.发现是Pythondir的配 ...
- css 去除点击之后的虚线
链接在被点击时会出现虚线框,即使松开了也仍然存在,在有的时候显得不美观.既然不好看,那就不要它.怎样去掉呢? 方法一 IE下可使用其私有的html属性:hideFoucs,在标签的结构中加入hidef ...
- ora-02429:无法删除用于强制唯一/主键的索引
今天打算删除orcale数据库中无用的表空间,发现报错,查资料删除,写个过程留着备用. 1.drop tablespace dldata INCLUDING CONTENTS CASCADE CONS ...
- 循序渐进Python3(十)-- 4 -- paramiko
paramiko模块,基于SSH用于连接远程服务器并执行相关操作. 使用 (1)SSHClient 用于连接远程服务器并执行基本命令 基于用户名密码连接: , ))transport.connect( ...
- Tomcat启动时项目重复加载,导致资源初始化两次的问题
http://blog.csdn.net/testcs_dn/article/details/38855641
- git操作日志
切换分支 git checkout plugin 更新文件 git pull origin master 合并分支 主分支合并到分支 git checkout dev_2 git merge mast ...