Just an update on the best workaround here… FORCE_ANSI_ALLOCATOR may not work well in the latest UE versions, and even if it does it may have unintended consequences (such as a reduction in performance).
Instead, I now recommend that you make a change to a script in the Xcode project. If you open Intermediate/ProjectFiles/[project name] (IOS).xcodeproj/project.pbxproj, you’ll see a section like this:
ED01E0DF7686EEC1CE350CF9 /* Generate dsym for archive, and strip */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"\"$(CONFIGURATION_BUILD_DIR)/$(EXECUTABLE_PATH)\"",
);
name = "Generate dsym for archive, and strip";
outputPaths = (
"\"$(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)\"",
);
runOnlyForDeploymentPostprocessing = 1;
shellPath = /bin/sh;
shellScript = "set -e\n\n# Run the wrapper dsym generator\n\"${UE_ENGINE_DIR}/Build/BatchFiles/Mac/GenerateUniversalDSYM.sh\" \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_PATH}\" \"${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}\"\nstrip -no_code_signature_warning -x -S -D \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_PATH}\"\n\n# Remove any unused architectures from dylibs in the .app (param1) that don't match the executable (param2). Also error if a dylib is missing arches\n\"${UE_ENGINE_DIR}/Build/BatchFiles/Mac/ThinApp.sh\" \"${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}\" \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_PATH}\"";
};
The important part is buried on the shellScript line and looks like this:
In other words, add the -x -S options to the strip command.
Now, if you archive / deploy your game again, you should no longer see the crash. You might need to edit a source file to force it to produce a new binary and re-run this script.
The reason for the crash is that UnrealBuildTool is (incorrectly, in my opinion) stripping operator new and operator delete symbols from the packaged binary. This means that code inside your game executable will use Unreal’s custom memory allocator, but code outside your game executable will use the default allocator. Specifically, any allocations in the C++ standard library that do not get inlined will use the default allocator. The crash is caused by allocating memory with Unreal’s custom allocator and then freeing it with the default allocator. It’s easy to trigger this by constructing something as simple as a std::string.
By adding these command-line arguments to strip, we tell it to only strip debug and local symbols. Global symbols like operator new / delete are left intact, ensuring that the std::string constructor and destructor, as well as all similar memory allocations, go through Unreal’s custom allocator.
More details of the problem here for anyone interested:
I’m going to report this to Epic as a bug, but this workaround should work well in the meantime.