Phenomenon
We've found operator new/delete override in iOS app, only works for the first time when the app launches on iOS16, operator override is not working in the second and subsequent launch of the same app.
Steps to reproduce
Development environment: XCode 16.2
Create a new iOS Objective-C project in XCode
In the project options page, choose the following settings:
- Name the project: OverrideNew
- Interface: Storyboard
- Language: Objective-C
- Testing System: None
Add test code
-
Change AppDelegate.m's file name to AppDelegate.mm to add the following C++ test code.
-
Add the following code after
#import "AppDelegate.h"
#include <os/log.h>
#include <string>
static bool needLog = false;
void* operator new(size_t size) {
void* ptr = malloc(size);
if(needLog) {
// Log to prove override new works
os_log_error(OS_LOG_DEFAULT, "Overrided new called. ptr: %p\n", ptr);
}
return ptr;
}
void operator delete(void* ptr) noexcept {
free(ptr);
if(needLog) {
// Log to prove override delete works
os_log_error(OS_LOG_DEFAULT, "Overrided delete called. ptr: %p\n", ptr);
}
}
void StringConstructTest(void) {
needLog = true;
os_log_error(OS_LOG_DEFAULT, "Enter StringConstructTest1\n");
{
std::string str;
// a long string will trigger memory allocation on heap
str = "Hello world and this is a long string.\n";
os_log_error(OS_LOG_DEFAULT, "%{public}s\n", str.c_str());
}
os_log_error(OS_LOG_DEFAULT, "Exit StringConstructTest1\n");
needLog = false;
}
- Call StringConstructTest() in didFinishLaunchingWithOptions method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
StringConstructTest();
return YES;
}
Change build settings
Change Minimum Deployments: iOS 16.
Build and run the project on an iOS16 device, emulator can not reproduce the problem.
Observe logs in Console app on Mac
Use the following log filters:
- message type: error
- process: OverrideNew
First launch
First launch on device(not from a XCode debug launch), the log is:
Enter StringConstructTest1
Overrided new called. ptr: 0x281f2f450
Hello world and this is a long string.
Overrided delete called. ptr: 0x281f2f450
Exit StringConstructTest1
"Overrided new called" proved the override new operator is called.
Second and subsequence launch
Second and subsequence launch on device(not from a XCode debug launch), the log is:
Enter StringConstructTest1
Hello world and this is a long string.
Exit StringConstructTest1
No log for "Overrided new called", the subsequence launch, the override operator new is not called anymore.
Expected behavior
For every app launch, log "Overrided new called" will happen and operator override works.
On iOS16, operator override only works for the first launch.
I've also tested on iOS18, operator override works every time as expected.
Question
Is there a way to force operator override works every time on iOS16?