# ScriptMethodMutable - **功能描述:** 把ScriptMethod的第一个const结构参数在调用上改成引用参数,函数内修改的值会保存下来。 - **使用位置:** UFUNCTION - **引擎模块:** Script - **元数据类型:** bool - **限制类型:** 第一个参数是结构类型 - **关联项:** [ScriptMethod](ScriptMethod.md) - **常用程度:** ★★ 把ScriptMethod的第一个const结构参数在调用上改成引用参数,函数内修改的值会保存下来。 - 在const参数上如果想改变值,依然要标记c++里的mutable。 - 虽然py生成的代码一模一样,但实际调用上ScriptMethodMutable会真正改变参数的值,而没有ScriptMethodMutable的函数并不会改变参数的原始值。 - ScriptMethodMutable和UPARAM(ref) 在调用效果上,都可以改变参数的值。但区别是UPARAM(ref) 生成的py代码会返回第一个参数作为返回值。 ```cpp USTRUCT(BlueprintType) struct INSIDER_API FMyPythonStruct_ScriptMethod { GENERATED_BODY() public: UPROPERTY(BlueprintReadWrite, EditAnywhere) mutable FString MyString; }; UCLASS(Blueprintable, BlueprintType) class INSIDER_API UMyPython_ScriptMethod_Test :public UObject { GENERATED_BODY() public: UFUNCTION(BlueprintCallable, meta = (ScriptMethod)) static void SetStringOnStruct(const FMyPythonStruct_ScriptMethod& myStruct, FString val); UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable)) static void SetStringOnStructMutable(const FMyPythonStruct_ScriptMethod& myStruct, FString val); UFUNCTION(BlueprintCallable, meta = (ScriptMethod, ScriptMethodMutable)) static void SetStringOnStructViaRef(UPARAM(ref) FMyPythonStruct_ScriptMethod& myStruct, FString val); }; ``` ## 测试效果: 看py里生成的代码是一致的,如果用UPARAM(ref),则在MyPython_ScriptMethod_Test里面生成的my_func_on_struct_via_ref会返回结构MyPythonStruct_ScriptMethod来达成引用的效果。 然而my_func_on_struct_mutable返回的是None,同不加ScriptMethodMutable的my_func_on_struct并没有区别。但是实际上在真正调用的时候会真正有区别。 ```cpp class MyPythonStruct_ScriptMethod(StructBase): r""" My Python Struct Script Method **C++ Source:** - **Module**: Insider - **File**: MyPython_ScriptMethod.h """ def __init__(self) -> None: ... def my_func_on_struct_via_ref(self, val: str) -> None: r""" x.my_func_on_struct_via_ref(val) -> None My Func on Struct Via Ref Args: val (str): """ ... def my_func_on_struct_mutable(self, val: str) -> None: r""" x.my_func_on_struct_mutable(val) -> None My Func on Struct Mutable Args: val (str): """ ... def my_func_on_struct(self, val: str) -> None: r""" x.my_func_on_struct(val) -> None My Func on Struct Args: val (str): """ ... class MyPython_ScriptMethod_Test(Object): r""" My Python Script Method Test **C++ Source:** - **Module**: Insider - **File**: MyPython_ScriptMethod.h """ @classmethod def my_func_on_struct_via_ref(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> MyPythonStruct_ScriptMethod: r""" X.my_func_on_struct_via_ref(my_struct, val) -> MyPythonStruct_ScriptMethod My Func on Struct Via Ref Args: my_struct (MyPythonStruct_ScriptMethod): val (str): Returns: MyPythonStruct_ScriptMethod: my_struct (MyPythonStruct_ScriptMethod): """ ... @classmethod def my_func_on_struct_mutable(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> None: r""" X.my_func_on_struct_mutable(my_struct, val) -> None My Func on Struct Mutable Args: my_struct (MyPythonStruct_ScriptMethod): val (str): """ ... @classmethod def my_func_on_struct(cls, my_struct: MyPythonStruct_ScriptMethod, val: str) -> None: r""" X.my_func_on_struct(my_struct, val) -> None My Func on Struct Args: my_struct (MyPythonStruct_ScriptMethod): val (str): """ ... ``` 在UE Python控制台里调用的记录,分析调用顺序: - 一开始调用set_string_on_struct_mutable,再print(a),可以打印出Hello,说明值真正的设置到了a结构里。 - 再尝试set_string_on_struct,再print(a),无法打印出FFF,说明值并没有设置到a结构里。说明py在调用的时候很可能构造了一个临时值来当作调对象,调用完成的新值并没有设置到a对象上。 - 再尝试set_string_on_struct_via_ref,再print(a),可以打印出First,说明用UPARAM(ref)可以也达成改变参数的效果。 ```cpp LogPython: a=unreal.MyPythonStruct_ScriptMethod() LogPython: print(a) LogPython: LogPython: a.set_string_on_struct_mutable("Hello") LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStructMutable LogPython: print(a) LogPython: LogPython: a.set_string_on_struct("FFF") LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStruct LogPython: print(a) LogPython: LogPython: a.set_string_on_struct_via_ref("First") LogBlueprintUserMessages: [None] UMyPython_ScriptMethod_Test::SetStringOnStructViaRef LogPython: print(a) LogPython: ``` ## 原理: 判断如果有ScriptMethodMutable,会设置SelfReturn,从而再最后把函数调用中的临时值复制给原本的参数值,达成可变引用调用的效果。 ```cpp // The function may have been flagged as mutable, in which case we always consider it to need a 'self' return if (!GeneratedWrappedDynamicMethod.SelfReturn.ParamProp && InFunc->HasMetaData(PyGenUtil::ScriptMethodMutableMetaDataKey)) { if (!SelfParam.ParamProp->IsA()) { REPORT_PYTHON_GENERATION_ISSUE(Error, TEXT("Function '%s.%s' is marked as 'ScriptMethodMutable' but the 'self' argument is not a struct."), *InFunc->GetOwnerClass()->GetName(), *InFunc->GetName()); return; } GeneratedWrappedDynamicMethod.SelfReturn = SelfParam; } ```